1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-02-09 00:18:00 +01:00

feat: add create and edit context screen (NEW) (#613)

* feat: add create and edit context screen

* feat: add edit button for contexts list

* fix: add legal values when press enter withou submit form

* fix: context form

Co-authored-by: Fredrik Strand Oseberg <fredrik.no@gmail.com>
This commit is contained in:
Youssef Khedher 2022-01-24 15:43:45 +01:00 committed by GitHub
parent 6a4fe7182a
commit 53cff04349
11 changed files with 758 additions and 70 deletions

View File

@ -0,0 +1,68 @@
import { makeStyles } from '@material-ui/core/styles';
export const useStyles = makeStyles(theme => ({
container: {
maxWidth: '470px',
},
form: {
display: 'flex',
flexDirection: 'column',
height: '100%',
},
input: { width: '100%', marginBottom: '1rem' },
inputHeader:{
marginBottom: '0.3rem'
},
label: {
minWidth: '300px',
[theme.breakpoints.down(600)]: {
minWidth: 'auto',
},
},
tagContainer: {
display: 'flex',
alignItems: 'flex-start',
marginBottom: '1rem'
},
tagInput: {
width: '75%',
marginRight: 'auto',
},
tagValue: {
marginRight: '3px',
marginBottom: '1rem'
},
buttonContainer: {
marginTop: 'auto',
display: 'flex',
justifyContent: 'flex-end',
},
cancelButton: {
marginRight: '1.5rem',
},
inputDescription: {
marginBottom: '0.5rem',
},
formHeader: {
fontWeight: 'normal',
marginTop: '0',
},
header: {
fontWeight: 'normal',
},
permissionErrorContainer: {
position: 'relative',
},
errorMessage: {
//@ts-ignore
fontSize: theme.fontSizes.smallBody,
color: theme.palette.error.main,
position: 'absolute',
top: '-8px',
},
switchContainer: {
display: 'flex',
alignItems: 'center',
marginLeft: '-9px'
},
}));

View File

@ -0,0 +1,197 @@
import Input from '../../common/Input/Input';
import { TextField, Button, Switch, Chip, Typography } from '@material-ui/core';
import { useStyles } from './ContextForm.styles';
import React, { useState } from 'react';
import { Add } from '@material-ui/icons';
import { trim } from '../../common/util';
interface IContextForm {
contextName: string;
contextDesc: string;
legalValues: Array<string>;
stickiness: boolean;
setContextName: React.Dispatch<React.SetStateAction<string>>;
setContextDesc: React.Dispatch<React.SetStateAction<string>>;
setStickiness: React.Dispatch<React.SetStateAction<boolean>>;
setLegalValues: React.Dispatch<React.SetStateAction<string[]>>;
handleSubmit: (e: any) => void;
handleCancel: () => void;
errors: { [key: string]: string };
mode: string;
clearErrors: () => void;
validateNameUniqueness: () => void;
setErrors: React.Dispatch<React.SetStateAction<Object>>;
}
const ENTER = 'Enter';
const ContextForm: React.FC<IContextForm> = ({
children,
handleSubmit,
handleCancel,
contextName,
contextDesc,
legalValues,
stickiness,
setContextName,
setContextDesc,
setLegalValues,
setStickiness,
errors,
mode,
validateNameUniqueness,
setErrors,
clearErrors,
}) => {
const styles = useStyles();
const [value, setValue] = useState('');
const [focused, setFocused] = useState(false);
const submit = (event: React.SyntheticEvent) => {
event.preventDefault();
if (focused) return;
handleSubmit(event);
};
const handleKeyDown = (event: React.KeyboardEvent) => {
if (event.key === ENTER && focused) {
addLegalValue();
return;
} else if (event.key === ENTER) {
handleSubmit(event);
}
};
const sortIgnoreCase = (a: string, b: string) => {
a = a.toLowerCase();
b = b.toLowerCase();
if (a === b) return 0;
if (a > b) return 1;
return -1;
};
const addLegalValue = () => {
clearErrors();
if (!value) {
return;
}
if (legalValues.indexOf(value) !== -1) {
setErrors(prev => ({
...prev,
tag: 'Duplicate legal value',
}));
return;
}
setLegalValues(prev => [...prev, trim(value)].sort(sortIgnoreCase));
setValue('');
};
const removeLegalValue = (index: number) => {
const filteredValues = legalValues.filter((_, i) => i !== index);
setLegalValues([...filteredValues]);
};
return (
<form onSubmit={submit} className={styles.form}>
<h3 className={styles.formHeader}>Context information</h3>
<div className={styles.container}>
<p className={styles.inputDescription}>
What is your context name?
</p>
<Input
className={styles.input}
label="Context name"
value={contextName}
disabled={mode === 'Edit'}
onChange={e => setContextName(e.target.value)}
error={Boolean(errors.name)}
errorText={errors.name}
onFocus={() => clearErrors()}
onBlur={validateNameUniqueness}
/>
<p className={styles.inputDescription}>
What is this context for?
</p>
<TextField
className={styles.input}
label="Context description"
variant="outlined"
multiline
maxRows={4}
value={contextDesc}
onChange={e => setContextDesc(e.target.value)}
/>
<p className={styles.inputDescription}>
Which values do you want to allow?
</p>
{legalValues.map((value, index) => {
return (
<Chip
key={index + value}
label={value}
className={styles.tagValue}
onDelete={() => removeLegalValue(index)}
title="Remove value"
/>
);
})}
<div className={styles.tagContainer}>
<TextField
label="Value"
name="value"
className={styles.tagInput}
value={value}
error={Boolean(errors.tag)}
helperText={errors.tag}
variant="outlined"
size="small"
onChange={e => setValue(trim(e.target.value))}
onKeyPress={e => handleKeyDown(e)}
onBlur={e => setFocused(false)}
onFocus={e => setFocused(true)}
/>
<Button
startIcon={<Add />}
onClick={addLegalValue}
variant="contained"
color="primary"
>
Add
</Button>
</div>
<p className={styles.inputHeader}>Custom stickiness (beta)</p>
<p>
By enabling stickiness on this context field you can use it
together with the flexible-rollout strategy. This will
guarantee a consistent behavior for specific values of this
context field. PS! Not all client SDK's support this feature
yet!{' '}
<a
href="https://docs.getunleash.io/advanced/stickiness"
target="_blank"
rel="noreferrer"
>
Read more
</a>
</p>
<div className={styles.switchContainer}>
<Switch
checked={stickiness}
value={stickiness}
onChange={() => setStickiness(!stickiness)}
/>
<Typography>{stickiness ? 'On' : 'Off'}</Typography>
</div>
</div>
<div className={styles.buttonContainer}>
<Button onClick={handleCancel} className={styles.cancelButton}>
Cancel
</Button>
{children}
</div>
</form>
);
};
export default ContextForm;

View File

@ -4,7 +4,8 @@ import HeaderTitle from '../../common/HeaderTitle';
import ConditionallyRender from '../../common/ConditionallyRender/ConditionallyRender'; import ConditionallyRender from '../../common/ConditionallyRender/ConditionallyRender';
import { import {
CREATE_CONTEXT_FIELD, CREATE_CONTEXT_FIELD,
DELETE_CONTEXT_FIELD, UPDATE_CONTEXT_FIELD, DELETE_CONTEXT_FIELD,
UPDATE_CONTEXT_FIELD,
} from '../../providers/AccessProvider/permissions'; } from '../../providers/AccessProvider/permissions';
import { import {
IconButton, IconButton,
@ -16,35 +17,78 @@ import {
useMediaQuery, useMediaQuery,
Button, Button,
} from '@material-ui/core'; } from '@material-ui/core';
import { Add, Album, Delete } from '@material-ui/icons'; import { Add, Album, Delete, Edit } from '@material-ui/icons';
import { useContext, useState } from 'react'; import { useContext, useState } from 'react';
import { Link } from 'react-router-dom'; import { Link, useHistory } from 'react-router-dom';
import { useStyles } from './styles'; import { useStyles } from './styles';
import ConfirmDialogue from '../../common/Dialogue'; import ConfirmDialogue from '../../common/Dialogue';
import AccessContext from '../../../contexts/AccessContext'; import AccessContext from '../../../contexts/AccessContext';
import useUnleashContext from '../../../hooks/api/getters/useUnleashContext/useUnleashContext';
import useContextsApi from '../../../hooks/api/actions/useContextsApi/useContextsApi';
import useToast from '../../../hooks/useToast';
const ContextList = ({ removeContextField, history, contextFields }) => { const ContextList = ({ removeContextField }) => {
const { hasAccess } = useContext(AccessContext); const { hasAccess } = useContext(AccessContext);
const [showDelDialogue, setShowDelDialogue] = useState(false); const [showDelDialogue, setShowDelDialogue] = useState(false);
const smallScreen = useMediaQuery('(max-width:700px)'); const smallScreen = useMediaQuery('(max-width:700px)');
const [name, setName] = useState(); const [name, setName] = useState();
const { context, refetch } = useUnleashContext();
const { removeContext } = useContextsApi();
const { setToastData, setToastApiError } = useToast();
const history = useHistory();
const styles = useStyles(); const styles = useStyles();
const onDeleteContext = async name => {
try {
await removeContext(name);
refetch();
setToastData({
type: 'success',
title: 'Successfully deleted context',
text: 'Your context is now deleted',
});
} catch (e) {
setToastApiError(e.toString());
}
setName(undefined);
setShowDelDialogue(false);
};
const contextList = () => const contextList = () =>
contextFields.map(field => ( context.map(field => (
<ListItem key={field.name} classes={{ root: styles.listItem }}> <ListItem key={field.name} classes={{ root: styles.listItem }}>
<ListItemIcon> <ListItemIcon>
<Album /> <Album />
</ListItemIcon> </ListItemIcon>
<ListItemText <ListItemText
primary={ primary={
<ConditionallyRender condition={hasAccess(UPDATE_CONTEXT_FIELD)} show={<Link to={`/context/edit/${field.name}`}> <ConditionallyRender
<strong>{field.name}</strong> condition={hasAccess(UPDATE_CONTEXT_FIELD)}
</Link> show={
} elseShow={<strong>{field.name}</strong>} />} <Link to={`/context/edit/${field.name}`}>
<strong>{field.name}</strong>
</Link>
}
elseShow={<strong>{field.name}</strong>}
/>
}
secondary={field.description} secondary={field.description}
/> />
<ConditionallyRender
condition={hasAccess(UPDATE_CONTEXT_FIELD)}
show={
<Tooltip title="Edit context field">
<IconButton
aria-label="edit"
onClick={() =>
history.push(`/context/edit/${field.name}`)
}
>
<Edit />
</IconButton>
</Tooltip>
}
/>
<ConditionallyRender <ConditionallyRender
condition={hasAccess(DELETE_CONTEXT_FIELD)} condition={hasAccess(DELETE_CONTEXT_FIELD)}
show={ show={
@ -102,18 +146,14 @@ const ContextList = ({ removeContextField, history, contextFields }) => {
> >
<List> <List>
<ConditionallyRender <ConditionallyRender
condition={contextFields.length > 0} condition={context.length > 0}
show={contextList} show={contextList}
elseShow={<ListItem>No context fields defined</ListItem>} elseShow={<ListItem>No context fields defined</ListItem>}
/> />
</List> </List>
<ConfirmDialogue <ConfirmDialogue
open={showDelDialogue} open={showDelDialogue}
onClick={() => { onClick={() => onDeleteContext(name)}
removeContextField({ name });
setName(undefined);
setShowDelDialogue(false);
}}
onClose={() => { onClose={() => {
setName(undefined); setName(undefined);
setShowDelDialogue(false); setShowDelDialogue(false);

View File

@ -0,0 +1,105 @@
import { useHistory } from 'react-router-dom';
import useUiConfig from '../../../hooks/api/getters/useUiConfig/useUiConfig';
import useToast from '../../../hooks/useToast';
import FormTemplate from '../../common/FormTemplate/FormTemplate';
import useContextForm from '../hooks/useContextForm';
import ContextForm from '../ContextForm/ContextForm';
import PermissionButton from '../../common/PermissionButton/PermissionButton';
import { CREATE_CONTEXT_FIELD } from '../../providers/AccessProvider/permissions';
import useContextsApi from '../../../hooks/api/actions/useContextsApi/useContextsApi';
import useUnleashContext from '../../../hooks/api/getters/useUnleashContext/useUnleashContext';
const CreateContext = () => {
const { setToastData, setToastApiError } = useToast();
const { uiConfig } = useUiConfig();
const history = useHistory();
const {
contextName,
contextDesc,
legalValues,
stickiness,
setContextName,
setContextDesc,
setLegalValues,
setStickiness,
getContextPayload,
validateNameUniqueness,
validateName,
clearErrors,
setErrors,
errors,
} = useContextForm();
const { createContext, loading } = useContextsApi();
const { refetch } = useUnleashContext();
const handleSubmit = async (e: Event) => {
e.preventDefault();
const validName = validateName();
if (validName) {
const payload = getContextPayload();
try {
await createContext(payload);
refetch();
history.push('/context');
setToastData({
title: 'Context created',
confetti: true,
type: 'success',
});
} catch (e: any) {
setToastApiError(e.toString());
}
}
};
const formatApiCode = () => {
return `curl --location --request POST '${
uiConfig.unleashUrl
}/api/admin/context' \\
--header 'Authorization: INSERT_API_KEY' \\
--header 'Content-Type: application/json' \\
--data-raw '${JSON.stringify(getContextPayload(), undefined, 2)}'`;
};
const handleCancel = () => {
history.goBack();
};
return (
<FormTemplate
loading={loading}
title="Create context"
description="Context fields are a basic building block used in Unleash to control roll-out.
They can be used together with strategy constraints as part of the activation strategy evaluation."
documentationLink="https://docs.getunleash.io/how-to/how-to-define-custom-context-fields"
formatApiCode={formatApiCode}
>
<ContextForm
errors={errors}
handleSubmit={handleSubmit}
handleCancel={handleCancel}
contextName={contextName}
setContextName={setContextName}
contextDesc={contextDesc}
setContextDesc={setContextDesc}
legalValues={legalValues}
setLegalValues={setLegalValues}
stickiness={stickiness}
setStickiness={setStickiness}
mode="Create"
validateNameUniqueness={validateNameUniqueness}
setErrors={setErrors}
clearErrors={clearErrors}
>
<PermissionButton
permission={CREATE_CONTEXT_FIELD}
type="submit"
>
Create context
</PermissionButton>
</ContextForm>
</FormTemplate>
);
};
export default CreateContext;

View File

@ -0,0 +1,117 @@
import { useEffect } from 'react';
import { useHistory, useParams } from 'react-router-dom';
import useContextsApi from '../../../hooks/api/actions/useContextsApi/useContextsApi';
import useContext from '../../../hooks/api/getters/useContext/useContext';
import useUiConfig from '../../../hooks/api/getters/useUiConfig/useUiConfig';
import useToast from '../../../hooks/useToast';
import FormTemplate from '../../common/FormTemplate/FormTemplate';
import PermissionButton from '../../common/PermissionButton/PermissionButton';
import { scrollToTop } from '../../common/util';
import { UPDATE_CONTEXT_FIELD } from '../../providers/AccessProvider/permissions';
import ContextForm from '../ContextForm/ContextForm';
import useContextForm from '../hooks/useContextForm';
const EditContext = () => {
useEffect(() => {
scrollToTop();
}, []);
const { uiConfig } = useUiConfig();
const { setToastData, setToastApiError } = useToast();
const { name } = useParams<{ name: string }>();
const { context, refetch } = useContext(name);
const { updateContext, loading } = useContextsApi();
const history = useHistory();
const {
contextName,
contextDesc,
legalValues,
stickiness,
setContextName,
setContextDesc,
setLegalValues,
setStickiness,
getContextPayload,
validateNameUniqueness,
validateName,
clearErrors,
setErrors,
errors,
} = useContextForm(
context?.name,
context?.description,
context?.legalValues,
context?.stickiness
);
const formatApiCode = () => {
return `curl --location --request PUT '${
uiConfig.unleashUrl
}/api/admin/context/${name}' \\
--header 'Authorization: INSERT_API_KEY' \\
--header 'Content-Type: application/json' \\
--data-raw '${JSON.stringify(getContextPayload(), undefined, 2)}'`;
};
const handleSubmit = async (e: Event) => {
e.preventDefault();
const payload = getContextPayload();
const validName = validateName();
if (validName) {
try {
await updateContext(payload);
refetch();
history.push('/context');
setToastData({
title: 'Context information updated',
type: 'success',
});
} catch (e: any) {
setToastApiError(e.toString());
}
}
};
const handleCancel = () => {
history.goBack();
};
return (
<FormTemplate
loading={loading}
title="Edit context"
description="Context fields are a basic building block used in Unleash to control roll-out.
They can be used together with strategy constraints as part of the activation strategy evaluation."
documentationLink="https://docs.getunleash.io/how-to/how-to-define-custom-context-fields"
formatApiCode={formatApiCode}
>
<ContextForm
errors={errors}
handleSubmit={handleSubmit}
handleCancel={handleCancel}
contextName={contextName}
setContextName={setContextName}
contextDesc={contextDesc}
setContextDesc={setContextDesc}
legalValues={legalValues}
setLegalValues={setLegalValues}
stickiness={stickiness}
setStickiness={setStickiness}
mode="Edit"
validateNameUniqueness={validateNameUniqueness}
setErrors={setErrors}
clearErrors={clearErrors}
>
<PermissionButton
permission={UPDATE_CONTEXT_FIELD}
type="submit"
>
Edit context
</PermissionButton>
</ContextForm>
</FormTemplate>
);
};
export default EditContext;

View File

@ -1,22 +0,0 @@
import { connect } from 'react-redux';
import ContextComponent from './form-context-component';
import { createContextField, validateName } from './../../store/context/actions';
const mapStateToProps = (state, props) => {
let contextField = { name: '', description: '', legalValues: [] };
if (props.contextFieldName) {
contextField = state.context.toJS().find(n => n.name === props.contextFieldName);
}
return {
contextField,
};
};
const mapDispatchToProps = dispatch => ({
validateName,
submit: contextField => createContextField(contextField)(dispatch),
});
const FormAddContainer = connect(mapStateToProps, mapDispatchToProps)(ContextComponent);
export default FormAddContainer;

View File

@ -1,26 +0,0 @@
import { connect } from 'react-redux';
import ContextComponent from './form-context-component';
import { updateContextField, validateName } from './../../store/context/actions';
const mapStateToProps = (state, props) => {
const contextFieldBase = { name: '', description: '', legalValues: [] };
const field = state.context.toJS().find(n => n.name === props.contextFieldName);
const contextField = Object.assign(contextFieldBase, field);
if (!field) {
contextField.initial = true;
}
return {
contextField,
};
};
const mapDispatchToProps = dispatch => ({
validateName,
submit: contextField => updateContextField(contextField)(dispatch),
editMode: true,
});
const FormAddContainer = connect(mapStateToProps, mapDispatchToProps)(ContextComponent);
export default FormAddContainer;

View File

@ -0,0 +1,88 @@
import { useEffect, useState } from 'react';
import useContextsApi from '../../../hooks/api/actions/useContextsApi/useContextsApi';
const useContextForm = (
initialcontextName = '',
initialcontextDesc = '',
initialLegalValues = [] as string[],
initialStickiness = false
) => {
const [contextName, setContextName] = useState(initialcontextName);
const [contextDesc, setContextDesc] = useState(initialcontextDesc);
const [legalValues, setLegalValues] = useState(initialLegalValues);
const [stickiness, setStickiness] = useState(initialStickiness);
const [errors, setErrors] = useState({});
const { validateContextName } = useContextsApi();
useEffect(() => {
setContextName(initialcontextName);
}, [initialcontextName]);
useEffect(() => {
setContextDesc(initialcontextDesc);
}, [initialcontextDesc]);
useEffect(() => {
setLegalValues(initialLegalValues);
// eslint-disable-next-line
}, [initialLegalValues.length]);
useEffect(() => {
setStickiness(initialStickiness);
}, [initialStickiness]);
const getContextPayload = () => {
return {
name: contextName,
description: contextDesc,
legalValues,
stickiness,
};
};
const NAME_EXISTS_ERROR = 'A context field with that name already exist';
const validateNameUniqueness = async () => {
try {
await validateContextName(contextName);
} catch (e: any) {
if (e.toString().includes(NAME_EXISTS_ERROR)) {
setErrors(prev => ({
...prev,
name: 'A context field with that name already exist',
}));
}
}
};
const validateName = () => {
if (contextName.length === 0) {
setErrors(prev => ({ ...prev, name: 'Name can not be empty.' }));
return false;
}
return true;
};
const clearErrors = () => {
setErrors({});
};
return {
contextName,
contextDesc,
legalValues,
stickiness,
setContextName,
setContextDesc,
setLegalValues,
setStickiness,
getContextPayload,
validateNameUniqueness,
validateName,
setErrors,
clearErrors,
errors,
};
};
export default useContextForm;

View File

@ -11,8 +11,6 @@ import Archive from '../../page/archive';
import Applications from '../../page/applications'; import Applications from '../../page/applications';
import ApplicationView from '../../page/applications/view'; import ApplicationView from '../../page/applications/view';
import ContextFields from '../../page/context'; import ContextFields from '../../page/context';
import CreateContextField from '../../page/context/create';
import EditContextField from '../../page/context/edit';
import ListTagTypes from '../../page/tag-types'; import ListTagTypes from '../../page/tag-types';
import Addons from '../../page/addons'; import Addons from '../../page/addons';
import AddonsCreate from '../../page/addons/create'; import AddonsCreate from '../../page/addons/create';
@ -42,7 +40,8 @@ import EditUser from '../admin/users/EditUser/EditUser';
import CreateApiToken from '../admin/api-token/CreateApiToken/CreateApiToken'; import CreateApiToken from '../admin/api-token/CreateApiToken/CreateApiToken';
import CreateEnvironment from '../environments/CreateEnvironment/CreateEnvironment'; import CreateEnvironment from '../environments/CreateEnvironment/CreateEnvironment';
import EditEnvironment from '../environments/EditEnvironment/EditEnvironment'; import EditEnvironment from '../environments/EditEnvironment/EditEnvironment';
import CreateContext from '../context/CreateContext/CreateContext';
import EditContext from '../context/EditContext/EditContext';
import EditTagType from '../tagTypes/EditTagType/EditTagType'; import EditTagType from '../tagTypes/EditTagType/EditTagType';
import CreateTagType from '../tagTypes/CreateTagType/CreateTagType'; import CreateTagType from '../tagTypes/CreateTagType/CreateTagType';
import EditProject from '../project/Project/EditProject/EditProject'; import EditProject from '../project/Project/EditProject/EditProject';
@ -205,7 +204,7 @@ export const routes = [
path: '/context/create', path: '/context/create',
parent: '/context', parent: '/context',
title: 'Create', title: 'Create',
component: CreateContextField, component: CreateContext,
type: 'protected', type: 'protected',
layout: 'main', layout: 'main',
flag: C, flag: C,
@ -215,7 +214,7 @@ export const routes = [
path: '/context/edit/:name', path: '/context/edit/:name',
parent: '/context', parent: '/context',
title: ':name', title: ':name',
component: EditContextField, component: EditContext,
type: 'protected', type: 'protected',
layout: 'main', layout: 'main',
flag: C, flag: C,

View File

@ -0,0 +1,81 @@
import { IContext } from '../../../../interfaces/context';
import useAPI from '../useApi/useApi';
const useContextsApi = () => {
const { makeRequest, createRequest, errors, loading } = useAPI({
propagateErrors: true,
});
const URI = 'api/admin/context';
const validateContextName = async (name: string) => {
const path = `${URI}/validate`;
const req = createRequest(path, {
method: 'POST',
body: JSON.stringify({ name }),
});
try {
const res = await makeRequest(req.caller, req.id);
return res;
} catch (e) {
throw e;
}
};
const createContext = async (payload: IContext) => {
const path = URI;
const req = createRequest(path, {
method: 'POST',
body: JSON.stringify(payload),
});
try {
const res = await makeRequest(req.caller, req.id);
return res;
} catch (e) {
throw e;
}
};
const updateContext = async (context: IContext) => {
const path = `${URI}/${context.name}`;
const req = createRequest(path, {
method: 'PUT',
body: JSON.stringify(context),
});
try {
const res = await makeRequest(req.caller, req.id);
return res;
} catch (e) {
throw e;
}
};
const removeContext = async (contextName: string) => {
const path = `${URI}/${contextName}`;
const req = createRequest(path, { method: 'DELETE' });
try {
const res = await makeRequest(req.caller, req.id);
return res;
} catch (e) {
throw e;
}
};
return {
createContext,
validateContextName,
updateContext,
removeContext,
errors,
loading
};
};
export default useContextsApi;

View File

@ -0,0 +1,41 @@
import useSWR, { mutate, SWRConfiguration } from 'swr';
import { useState, useEffect } from 'react';
import { formatApiPath } from '../../../../utils/format-path';
import handleErrorResponses from '../httpErrorResponseHandler';
const useContext = (name: string, options: SWRConfiguration = {}) => {
const fetcher = async () => {
const path = formatApiPath(`api/admin/context/${name}`);
return fetch(path, {
method: 'GET',
})
.then(handleErrorResponses('Context data'))
.then(res => res.json());
};
const FEATURE_CACHE_KEY = `api/admin/context/${name}`;
const { data, error } = useSWR(FEATURE_CACHE_KEY, fetcher, {
...options,
});
const [loading, setLoading] = useState(!error && !data);
const refetch = () => {
mutate(FEATURE_CACHE_KEY);
};
useEffect(() => {
setLoading(!error && !data);
}, [data, error]);
return {
context: data || { name: '', description: '', legalValues: [], stickiness: false },
error,
loading,
refetch,
FEATURE_CACHE_KEY,
};
};
export default useContext;