mirror of
https://github.com/strapi/strapi.git
synced 2025-09-26 00:39:49 +00:00
Apply PR review
This commit is contained in:
parent
e250ac81d5
commit
7f60f6eb57
@ -15,7 +15,6 @@ const Wrapper = styled.div`
|
||||
line-height: normal;
|
||||
}
|
||||
p {
|
||||
line-height: normal;
|
||||
&:first-of-type {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
|
@ -5,13 +5,14 @@
|
||||
*/
|
||||
|
||||
import styled from 'styled-components';
|
||||
import { sizes } from '@buffetjs/styles';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
padding-top: 3px;
|
||||
padding-bottom: 8px;
|
||||
table {
|
||||
width: 100%;
|
||||
border-radius: 3px;
|
||||
border-radius: ${sizes.borderRadius};
|
||||
overflow: hidden;
|
||||
}
|
||||
tr {
|
||||
@ -45,8 +46,8 @@ const Wrapper = styled.div`
|
||||
}
|
||||
}
|
||||
tbody {
|
||||
border-bottom-left-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
border-bottom-left-radius: ${sizes.borderRadius};
|
||||
border-bottom-right-radius: ${sizes.borderRadius};
|
||||
box-shadow: inset 0px 0px 0px 1px #f6f6f6;
|
||||
td {
|
||||
height: 54px;
|
||||
|
@ -5,13 +5,14 @@
|
||||
*/
|
||||
|
||||
import styled from 'styled-components';
|
||||
import { sizes } from '@buffetjs/styles';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
margin-top: 12px;
|
||||
padding: 23px 24px 26px 24px;
|
||||
background-color: #fafafb;
|
||||
border-radius: 3px;
|
||||
ul {
|
||||
border-radius: ${sizes.borderRadius};
|
||||
> ul {
|
||||
margin-bottom: 0;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
@ -29,67 +30,68 @@ const Wrapper = styled.div`
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
li {
|
||||
position: relative;
|
||||
padding-right: 30px;
|
||||
&:not(:first-of-type) {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
&:last-of-type {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
> section {
|
||||
display: inline-block;
|
||||
width: 50%;
|
||||
vertical-align: top;
|
||||
&:nth-child(odd) {
|
||||
padding-right: 15px;
|
||||
li {
|
||||
position: relative;
|
||||
padding-right: 30px;
|
||||
&:not(:first-of-type) {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
&:nth-child(even) {
|
||||
padding-left: 15px;
|
||||
&:last-of-type {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
> p {
|
||||
font-size: 13px;
|
||||
color: #333740;
|
||||
font-weight: 500;
|
||||
}
|
||||
> div:first-of-type {
|
||||
height: 34px;
|
||||
> div:first-of-type {
|
||||
height: 34px;
|
||||
min-height: 34px;
|
||||
border: 1px solid #e3e9f3;
|
||||
border-radius: 2px;
|
||||
> section {
|
||||
display: inline-block;
|
||||
width: 50%;
|
||||
vertical-align: top;
|
||||
&:nth-child(odd) {
|
||||
padding-right: 15px;
|
||||
}
|
||||
&:nth-child(even) {
|
||||
padding-left: 15px;
|
||||
}
|
||||
> p {
|
||||
font-size: 13px;
|
||||
color: #333740;
|
||||
align-items: normal;
|
||||
font-weight: 500;
|
||||
}
|
||||
> div:first-of-type {
|
||||
height: 34px;
|
||||
> div:first-of-type {
|
||||
height: 32px;
|
||||
padding: 0 1rem;
|
||||
height: 34px;
|
||||
min-height: 34px;
|
||||
border: 1px solid #e3e9f3;
|
||||
border-radius: 2px;
|
||||
font-size: 13px;
|
||||
color: #333740;
|
||||
align-items: normal;
|
||||
> div:first-of-type {
|
||||
height: 32px;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
> div:last-of-type {
|
||||
display: none;
|
||||
}
|
||||
&:hover {
|
||||
cursor: text;
|
||||
}
|
||||
}
|
||||
> div:last-of-type {
|
||||
display: none;
|
||||
}
|
||||
&:hover {
|
||||
cursor: text;
|
||||
> span + div:first-of-type {
|
||||
border-color: #78caff;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
> span + div:first-of-type {
|
||||
border-color: #78caff;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
& + div {
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
right: 0;
|
||||
button {
|
||||
margin: 0;
|
||||
& + div {
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
right: 0;
|
||||
button {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bordered {
|
||||
input {
|
||||
border: 1px solid #f64d0a;
|
||||
|
@ -7,6 +7,7 @@ import { CircleButton } from 'strapi-helper-plugin';
|
||||
import { InputText } from '@buffetjs/core';
|
||||
import { Plus } from '@buffetjs/icons';
|
||||
|
||||
import borderColor from './utils/borderColor';
|
||||
import keys from './keys';
|
||||
import Wrapper from './Wrapper';
|
||||
|
||||
@ -19,8 +20,8 @@ const HeadersInput = ({
|
||||
onRemove,
|
||||
value,
|
||||
}) => {
|
||||
const optionFormat = value => ({ value: value, label: value });
|
||||
const options = keys.map(key => optionFormat(key));
|
||||
const formatOption = value => ({ value: value, label: value });
|
||||
const options = keys.map(key => formatOption(key));
|
||||
|
||||
const handleBlur = () => onBlur({ target: { name, value } });
|
||||
|
||||
@ -33,28 +34,14 @@ const HeadersInput = ({
|
||||
}
|
||||
};
|
||||
|
||||
const handleClick = () => onClick(name);
|
||||
|
||||
const handleRemoveItem = index => {
|
||||
const event = index === 0 && value.length === 1 ? 'clear' : 'remove';
|
||||
onRemove({ event, index });
|
||||
};
|
||||
|
||||
const customStyles = hasError => {
|
||||
const selectBorder = isFocused => {
|
||||
if (isFocused) {
|
||||
return '1px solid #78caff !important';
|
||||
}
|
||||
if (hasError) {
|
||||
return '1px solid #F64D0A !important';
|
||||
}
|
||||
return '1px solid #E3E9F3 !important';
|
||||
};
|
||||
|
||||
return {
|
||||
control: (base, state) => ({
|
||||
...base,
|
||||
border: selectBorder(state.isFocused),
|
||||
border: `1px solid ${borderColor({
|
||||
isFocused: state.isFocused,
|
||||
hasError,
|
||||
})} !important`,
|
||||
borderRadius: '2px !important',
|
||||
}),
|
||||
menu: base => {
|
||||
@ -121,7 +108,7 @@ const HeadersInput = ({
|
||||
name={`${name}.${index}.key`}
|
||||
options={options}
|
||||
styles={customStyles(entryErrors && entryErrors.key)}
|
||||
value={optionFormat(key)}
|
||||
value={formatOption(key)}
|
||||
/>
|
||||
</section>
|
||||
<section>
|
||||
@ -137,14 +124,14 @@ const HeadersInput = ({
|
||||
<CircleButton
|
||||
type="button"
|
||||
isRemoveButton
|
||||
onClick={() => handleRemoveItem(index)}
|
||||
onClick={() => onRemove(index)}
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
<button onClick={handleClick} type="button">
|
||||
<button onClick={() => onClick(name)} type="button">
|
||||
<Plus fill="#007eff" width="10px" />
|
||||
<FormattedMessage id="Settings.webhooks.create.header" />
|
||||
</button>
|
||||
|
@ -0,0 +1,11 @@
|
||||
const borderColor = ({ isFocused = false, hasError = false }) => {
|
||||
if (isFocused) {
|
||||
return '#78caff';
|
||||
}
|
||||
if (hasError) {
|
||||
return '#F64D0A';
|
||||
}
|
||||
return '#E3E9F3';
|
||||
};
|
||||
|
||||
export default borderColor;
|
@ -7,7 +7,7 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { ErrorMessage, Label } from '@buffetjs/styles';
|
||||
import { Error, InputText } from '@buffetjs/core';
|
||||
import { Error } from '@buffetjs/core';
|
||||
|
||||
import HeadersInput from '../HeadersInput';
|
||||
import EventInput from '../EventInput';
|
||||
@ -71,29 +71,15 @@ function Inputs({
|
||||
|
||||
return (
|
||||
<>
|
||||
{type === 'events' ? (
|
||||
<EventInput
|
||||
name={name}
|
||||
onChange={e => {
|
||||
handleChange(e);
|
||||
onBlur(e);
|
||||
}}
|
||||
value={value}
|
||||
/>
|
||||
) : (
|
||||
<InputText
|
||||
className={hasError ? 'hasError' : ''}
|
||||
name={name}
|
||||
onBlur={onBlur}
|
||||
onChange={handleChange}
|
||||
value={value}
|
||||
/>
|
||||
)}
|
||||
{hasError && (
|
||||
<ErrorMessage>
|
||||
<FormattedMessage id={error} />
|
||||
</ErrorMessage>
|
||||
)}
|
||||
<EventInput
|
||||
name={name}
|
||||
onChange={e => {
|
||||
handleChange(e);
|
||||
onBlur(e);
|
||||
}}
|
||||
value={value}
|
||||
/>
|
||||
{hasError && <ErrorMessage>{error}</ErrorMessage>}
|
||||
</>
|
||||
);
|
||||
}}
|
||||
|
@ -28,7 +28,7 @@ function ListRow({
|
||||
const links = [
|
||||
{
|
||||
icon: 'pencil',
|
||||
onClick: () => handleEditClick(),
|
||||
onClick: () => onEditClick(id),
|
||||
},
|
||||
{
|
||||
icon: 'trash',
|
||||
@ -41,26 +41,19 @@ function ListRow({
|
||||
|
||||
const isChecked = itemsToDelete.includes(id);
|
||||
|
||||
const handleEditClick = () => onEditClick(id);
|
||||
|
||||
const handleEnabledChange = ({ target: { value } }) =>
|
||||
onEnabledChange(value, id);
|
||||
|
||||
const handleCheckChange = ({ target: { value } }) => onCheckChange(value, id);
|
||||
|
||||
const handleDeleteConfirm = () => {
|
||||
onDeleteCLick(id);
|
||||
const handleDeleteConfirm = async () => {
|
||||
await onDeleteCLick(id);
|
||||
setShowModal(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledListRow onClick={handleEditClick}>
|
||||
<StyledListRow onClick={() => onEditClick(id)}>
|
||||
<td>
|
||||
<Checkbox
|
||||
name={name}
|
||||
value={isChecked}
|
||||
onClick={e => e.stopPropagation()}
|
||||
onChange={handleCheckChange}
|
||||
onChange={({ target: { value } }) => onCheckChange(value, id)}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
@ -74,7 +67,7 @@ function ListRow({
|
||||
<Switch
|
||||
name={name}
|
||||
value={isEnabled}
|
||||
onChange={handleEnabledChange}
|
||||
onChange={({ target: { value } }) => onEnabledChange(value, id)}
|
||||
></Switch>
|
||||
</div>
|
||||
</td>
|
||||
|
@ -15,13 +15,17 @@ const TriggerContainer = ({ isPending, onCancel, response }) => {
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<p>Test-trigger</p>
|
||||
<p>
|
||||
{formatMessage({
|
||||
id: `Settings.webhooks.trigger.test`,
|
||||
})}
|
||||
</p>
|
||||
</td>
|
||||
{isPending && (
|
||||
<>
|
||||
<td>
|
||||
<p>
|
||||
<Pending fill="#6DBB1A" width="15px" height="15px" />
|
||||
<Pending fill="#ffb500" width="15px" height="15px" />
|
||||
<span>
|
||||
{formatMessage({
|
||||
id: `Settings.webhooks.trigger.pending`,
|
||||
@ -65,7 +69,12 @@ const TriggerContainer = ({ isPending, onCancel, response }) => {
|
||||
<td>
|
||||
<p className="fail-label">
|
||||
<Fail fill="#f64d0a" width="15px" height="15px" />
|
||||
<span>Error {statusCode}</span>
|
||||
<span>
|
||||
{formatMessage({
|
||||
id: `Settings.error`,
|
||||
})}{' '}
|
||||
{statusCode}
|
||||
</span>
|
||||
</p>
|
||||
</td>
|
||||
<td>
|
||||
|
@ -113,10 +113,6 @@ export class Admin extends React.Component {
|
||||
}, []);
|
||||
};
|
||||
|
||||
renderMarketPlace = props => <Marketplace {...props} {...this.props} />;
|
||||
|
||||
renderSettings = props => <SettingsPage {...props} {...this.props} />;
|
||||
|
||||
renderPluginDispatcher = props => {
|
||||
// NOTE: Send the needed props instead of everything...
|
||||
|
||||
@ -183,10 +179,12 @@ export class Admin extends React.Component {
|
||||
/>
|
||||
<Route
|
||||
path="/marketplace"
|
||||
render={this.renderMarketPlace}
|
||||
exact
|
||||
render={props => this.renderRoute(props, Marketplace)}
|
||||
/>
|
||||
<Route
|
||||
path="/settings"
|
||||
render={props => this.renderRoute(props, SettingsPage)}
|
||||
/>
|
||||
<Route path="/settings" render={this.renderSettings} />
|
||||
<Route key="7" path="" component={NotFoundPage} />
|
||||
<Route key="8" path="404" component={NotFoundPage} />
|
||||
</Switch>
|
||||
|
@ -32,9 +32,7 @@ function SettingsPage() {
|
||||
<div className="col-md-3">
|
||||
<LeftMenu>
|
||||
{menuItems.map(item => {
|
||||
return (
|
||||
<LeftMenuList {...item} key={JSON.stringify(item.title)} />
|
||||
);
|
||||
return <LeftMenuList {...item} key={item.title.id} />;
|
||||
})}
|
||||
</LeftMenu>
|
||||
</div>
|
||||
|
@ -32,6 +32,8 @@ const Wrapper = styled.div`
|
||||
}
|
||||
span svg {
|
||||
margin-top: -2px;
|
||||
margin-right: -10px;
|
||||
margin-bottom: -5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,9 +5,9 @@
|
||||
*/
|
||||
|
||||
import React, { useEffect, useReducer, useCallback, useState } from 'react';
|
||||
import { useHistory, useLocation } from 'react-router-dom';
|
||||
import { get, isEmpty, isEqual, set, setWith } from 'lodash';
|
||||
import { Header } from '@buffetjs/custom';
|
||||
import { useHistory, useParams } from 'react-router-dom';
|
||||
import { get, isEmpty, isEqual, set } from 'lodash';
|
||||
import { Header, Inputs as InputsIndex } from '@buffetjs/custom';
|
||||
import { Play } from '@buffetjs/icons';
|
||||
import {
|
||||
request,
|
||||
@ -21,7 +21,8 @@ import TriggerContainer from '../../../components/TriggerContainer';
|
||||
|
||||
import reducer, { initialState } from './reducer';
|
||||
import form from './utils/form';
|
||||
import createYupSchema from './utils/schema';
|
||||
import schema from './utils/schema';
|
||||
import { cleanHeaders, cleanData, cleanErrors } from './utils/formatData';
|
||||
|
||||
import Wrapper from './Wrapper';
|
||||
|
||||
@ -29,31 +30,105 @@ function EditView() {
|
||||
const { formatMessage } = useGlobalContext();
|
||||
const [submittedOnce, setSubmittedOnce] = useState(false);
|
||||
const [reducerState, dispatch] = useReducer(reducer, initialState);
|
||||
const location = useLocation();
|
||||
const { push } = useHistory();
|
||||
|
||||
const {
|
||||
formErrors,
|
||||
modifiedWebhook,
|
||||
initialWebhook,
|
||||
modifiedData,
|
||||
initialData,
|
||||
isTriggering,
|
||||
triggerResponse,
|
||||
} = reducerState.toJS();
|
||||
|
||||
const { name } = modifiedWebhook;
|
||||
|
||||
const id = location.pathname.split('/')[3];
|
||||
const { name } = modifiedData;
|
||||
const { id } = useParams();
|
||||
const isCreating = id === 'create';
|
||||
|
||||
const abortController = new AbortController();
|
||||
|
||||
const { signal } = abortController;
|
||||
|
||||
useEffect(() => {
|
||||
if (!isCreating) {
|
||||
fetchData();
|
||||
|
||||
return () => {
|
||||
abortController.abort();
|
||||
};
|
||||
}
|
||||
}, [fetchData, isCreating]);
|
||||
}, [abortController, fetchData, isCreating]);
|
||||
|
||||
/* Header props */
|
||||
|
||||
const areActionDisabled =
|
||||
isEqual(initialData, modifiedData) || Object.keys(formErrors).length > 0;
|
||||
|
||||
const headerTitle = isCreating
|
||||
? formatMessage({
|
||||
id: `Settings.webhooks.create`,
|
||||
})
|
||||
: name;
|
||||
|
||||
const isTriggerActionDisabled =
|
||||
isCreating || (!isCreating && !areActionDisabled) || isTriggering;
|
||||
|
||||
const actions = [
|
||||
{
|
||||
color: 'primary',
|
||||
disabled: isTriggerActionDisabled,
|
||||
label: formatMessage({
|
||||
id: `Settings.webhooks.trigger`,
|
||||
}),
|
||||
onClick: handleTrigger,
|
||||
style: {
|
||||
padding: '0 15px',
|
||||
},
|
||||
title: isTriggerActionDisabled
|
||||
? formatMessage({
|
||||
id: `Settings.webhooks.trigger.save`,
|
||||
})
|
||||
: null,
|
||||
type: 'button',
|
||||
icon: (
|
||||
<Play
|
||||
width="14px"
|
||||
height="14px"
|
||||
fill={isTriggerActionDisabled ? '#b4b6ba' : '#ffffff'}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
color: 'cancel',
|
||||
disabled: areActionDisabled,
|
||||
label: formatMessage({
|
||||
id: `app.components.Button.reset`,
|
||||
}),
|
||||
onClick: handleReset,
|
||||
style: {
|
||||
padding: '0 20px',
|
||||
},
|
||||
type: 'button',
|
||||
},
|
||||
{
|
||||
color: 'success',
|
||||
disabled: areActionDisabled,
|
||||
label: formatMessage({
|
||||
id: `app.components.Button.save`,
|
||||
}),
|
||||
style: {
|
||||
minWidth: 140,
|
||||
},
|
||||
type: 'submit',
|
||||
},
|
||||
];
|
||||
|
||||
const headerProps = {
|
||||
title: {
|
||||
label: headerTitle,
|
||||
},
|
||||
actions: actions,
|
||||
};
|
||||
|
||||
/* Data methods */
|
||||
|
||||
const fetchData = useCallback(async () => {
|
||||
try {
|
||||
@ -72,115 +147,57 @@ function EditView() {
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
const headerTitle = isCreating
|
||||
? formatMessage({
|
||||
id: `Settings.webhooks.create`,
|
||||
})
|
||||
: name;
|
||||
|
||||
const actionsAreDisabled =
|
||||
isEqual(initialWebhook, modifiedWebhook) ||
|
||||
Object.keys(formErrors).length > 0;
|
||||
|
||||
const triggerActionIsDisabled =
|
||||
isCreating || (!isCreating && !actionsAreDisabled);
|
||||
|
||||
const handleTrigger = async () => {
|
||||
dispatch({
|
||||
type: 'ON_TRIGGER',
|
||||
});
|
||||
|
||||
try {
|
||||
const { data } = await request(`/admin/webhooks/${id}/trigger`, {
|
||||
method: 'POST',
|
||||
signal,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: 'TRIGGER_SUCCEEDED',
|
||||
response: data,
|
||||
});
|
||||
} catch (err) {
|
||||
if (err.code !== 20) {
|
||||
strapi.notification.error('notification.error');
|
||||
}
|
||||
const submitForm = () => {
|
||||
if (!isCreating) {
|
||||
updateWebhook();
|
||||
} else {
|
||||
createWebhooks();
|
||||
}
|
||||
};
|
||||
|
||||
const onCancelTrigger = () => {
|
||||
abortController.abort();
|
||||
const createWebhooks = async () => {
|
||||
try {
|
||||
await request(`/admin/webhooks`, {
|
||||
method: 'POST',
|
||||
body: cleanData(modifiedData),
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: 'ON_TRIGGER_CANCELED',
|
||||
});
|
||||
strapi.notification.success(`notification.success`);
|
||||
goBack();
|
||||
} catch (err) {
|
||||
strapi.notification.error('notification.error');
|
||||
}
|
||||
};
|
||||
|
||||
const updateWebhook = async () => {
|
||||
try {
|
||||
const body = cleanData(modifiedData);
|
||||
delete body.id;
|
||||
|
||||
await request(`/admin/webhooks/${id}`, {
|
||||
method: 'PUT',
|
||||
body,
|
||||
});
|
||||
|
||||
fetchData();
|
||||
|
||||
strapi.notification.error('notification.form.success.fields');
|
||||
} catch (err) {
|
||||
strapi.notification.error('notification.error');
|
||||
}
|
||||
};
|
||||
|
||||
/* Form user interactions */
|
||||
|
||||
const handleReset = () =>
|
||||
dispatch({
|
||||
type: 'RESET',
|
||||
type: 'RESET_FORM',
|
||||
});
|
||||
|
||||
const actions = [
|
||||
{
|
||||
color: 'primary',
|
||||
disabled: triggerActionIsDisabled,
|
||||
label: formatMessage({
|
||||
id: `Settings.webhooks.trigger`,
|
||||
}),
|
||||
onClick: () => {
|
||||
handleTrigger();
|
||||
},
|
||||
style: {
|
||||
padding: '0 15px',
|
||||
},
|
||||
title: triggerActionIsDisabled
|
||||
? formatMessage({
|
||||
id: `Settings.webhooks.trigger.save`,
|
||||
})
|
||||
: null,
|
||||
type: 'button',
|
||||
icon: (
|
||||
<Play
|
||||
width="6px"
|
||||
height="7px"
|
||||
fill={triggerActionIsDisabled ? '#b4b6ba' : '#ffffff'}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
color: 'cancel',
|
||||
disabled: actionsAreDisabled,
|
||||
label: formatMessage({
|
||||
id: `app.components.Button.reset`,
|
||||
}),
|
||||
onClick: () => handleReset(),
|
||||
style: {
|
||||
padding: '0 20px',
|
||||
},
|
||||
type: 'button',
|
||||
},
|
||||
{
|
||||
color: 'success',
|
||||
disabled: actionsAreDisabled,
|
||||
label: formatMessage({
|
||||
id: `app.components.Button.save`,
|
||||
}),
|
||||
style: {
|
||||
minWidth: 140,
|
||||
},
|
||||
type: 'submit',
|
||||
},
|
||||
];
|
||||
|
||||
const headerProps = {
|
||||
title: {
|
||||
label: headerTitle,
|
||||
},
|
||||
actions: actions,
|
||||
};
|
||||
|
||||
const handleBlur = () => {
|
||||
if (submittedOnce) checkFormErrors();
|
||||
if (submittedOnce) {
|
||||
checkFormErrors();
|
||||
}
|
||||
};
|
||||
|
||||
const handleChange = ({ target: { name, value } }) => {
|
||||
@ -198,11 +215,10 @@ function EditView() {
|
||||
});
|
||||
};
|
||||
|
||||
const handleRemove = ({ event, index }) => {
|
||||
const handleRemove = index => {
|
||||
dispatch({
|
||||
type: 'ON_HEADER_REMOVE',
|
||||
index,
|
||||
event,
|
||||
});
|
||||
resetError('headers');
|
||||
};
|
||||
@ -213,20 +229,14 @@ function EditView() {
|
||||
checkFormErrors(true);
|
||||
};
|
||||
|
||||
const submitForm = () => {
|
||||
if (!isCreating) {
|
||||
updateWebhook();
|
||||
} else {
|
||||
createWebhooks();
|
||||
}
|
||||
};
|
||||
/* Validations */
|
||||
|
||||
const checkFormErrors = async (submit = false) => {
|
||||
const webhookToCheck = modifiedWebhook;
|
||||
set(webhookToCheck, 'headers', cleanHeaders());
|
||||
const webhookToCheck = modifiedData;
|
||||
set(webhookToCheck, 'headers', cleanHeaders(modifiedData.headers));
|
||||
|
||||
try {
|
||||
await createYupSchema(form).validate(webhookToCheck, {
|
||||
await schema.validate(webhookToCheck, {
|
||||
abortEarly: false,
|
||||
});
|
||||
|
||||
@ -238,18 +248,6 @@ function EditView() {
|
||||
}
|
||||
};
|
||||
|
||||
const cleanHeaders = () => {
|
||||
const { headers } = modifiedWebhook;
|
||||
|
||||
if (Object.keys(headers).length === 1) {
|
||||
const { key, value } = headers[0];
|
||||
if (key.length === 0 && value.length === 0) return [];
|
||||
}
|
||||
return headers;
|
||||
};
|
||||
|
||||
const goBack = () => push('/settings/webhooks');
|
||||
|
||||
const resetError = name => {
|
||||
const errors = formErrors;
|
||||
|
||||
@ -260,68 +258,66 @@ function EditView() {
|
||||
};
|
||||
|
||||
const setErrors = errors => {
|
||||
const newErrors = Object.keys(errors).reduce((acc, curr) => {
|
||||
const { id } = errors[curr];
|
||||
|
||||
setWith(acc, curr, id ? id : errors[curr], Object);
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
dispatch({
|
||||
type: 'SET_ERRORS',
|
||||
errors: newErrors,
|
||||
errors: cleanErrors(errors),
|
||||
});
|
||||
};
|
||||
|
||||
const createWebhooks = async () => {
|
||||
const errorMessage = error => {
|
||||
if (!error) {
|
||||
return null;
|
||||
}
|
||||
if (typeof error === 'string') {
|
||||
return formatMessage({
|
||||
id: error,
|
||||
});
|
||||
}
|
||||
return error;
|
||||
};
|
||||
|
||||
/* Trigger events */
|
||||
|
||||
const handleTrigger = async () => {
|
||||
dispatch({
|
||||
type: 'IS_TRIGGERING',
|
||||
});
|
||||
|
||||
try {
|
||||
await request(`/admin/webhooks`, {
|
||||
const { data } = await request(`/admin/webhooks/${id}/trigger`, {
|
||||
method: 'POST',
|
||||
body: formatWebhook(),
|
||||
signal,
|
||||
});
|
||||
|
||||
strapi.notification.success(`notification.success`);
|
||||
goBack();
|
||||
dispatch({
|
||||
type: 'TRIGGER_SUCCEEDED',
|
||||
response: data,
|
||||
});
|
||||
} catch (err) {
|
||||
strapi.notification.error('notification.error');
|
||||
if (err.code !== 20) {
|
||||
strapi.notification.error('notification.error');
|
||||
}
|
||||
dispatch({
|
||||
type: 'IS_TRIGGERING',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const updateWebhook = async () => {
|
||||
try {
|
||||
const body = formatWebhook();
|
||||
delete body.id;
|
||||
const onCancelTrigger = () => {
|
||||
abortController.abort();
|
||||
|
||||
await request(`/admin/webhooks/${id}`, {
|
||||
method: 'PUT',
|
||||
body,
|
||||
});
|
||||
|
||||
fetchData();
|
||||
|
||||
strapi.notification.error('notification.form.success.fields');
|
||||
} catch (err) {
|
||||
strapi.notification.error('notification.error');
|
||||
}
|
||||
dispatch({
|
||||
type: 'ON_TRIGGER_CANCELED',
|
||||
});
|
||||
};
|
||||
|
||||
// utils
|
||||
const formatWebhook = () => {
|
||||
const webhooks = modifiedWebhook;
|
||||
set(webhooks, 'headers', unformatHeaders(cleanHeaders()));
|
||||
return webhooks;
|
||||
};
|
||||
/* Nav */
|
||||
|
||||
const unformatHeaders = headers => {
|
||||
return headers.reduce((obj, item) => {
|
||||
const { key, value } = item;
|
||||
return {
|
||||
...obj,
|
||||
[key]: value,
|
||||
};
|
||||
}, {});
|
||||
};
|
||||
const goBack = () => push('/settings/webhooks');
|
||||
|
||||
const renderHeadersInput = () => props => (
|
||||
<Inputs {...props} onClick={handleClick} onRemove={handleRemove} />
|
||||
);
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
@ -343,16 +339,18 @@ function EditView() {
|
||||
{Object.keys(form).map(key => {
|
||||
return (
|
||||
<div key={key} className={form[key].styleName}>
|
||||
<Inputs
|
||||
<InputsIndex
|
||||
{...form[key]}
|
||||
error={get(formErrors, key, null)}
|
||||
customInputs={{
|
||||
headers: renderHeadersInput(),
|
||||
events: Inputs,
|
||||
}}
|
||||
error={errorMessage(get(formErrors, key, null))}
|
||||
name={key}
|
||||
onBlur={handleBlur}
|
||||
onChange={handleChange}
|
||||
onClick={handleClick}
|
||||
onRemove={handleRemove}
|
||||
validations={form[key].validations}
|
||||
value={modifiedWebhook[key] || form[key].value}
|
||||
value={modifiedData[key] || form[key].value}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,79 +1,74 @@
|
||||
import { fromJS } from 'immutable';
|
||||
import { cloneDeep, set, get } from 'lodash';
|
||||
import { get } from 'lodash';
|
||||
|
||||
const header = { key: '', value: '' };
|
||||
|
||||
const initialWebhook = {
|
||||
events: [],
|
||||
headers: [header],
|
||||
name: null,
|
||||
url: null,
|
||||
headers: [{ key: '', value: '' }],
|
||||
events: [],
|
||||
};
|
||||
|
||||
const initialState = fromJS({
|
||||
formErrors: {},
|
||||
initialWebhook: initialWebhook,
|
||||
modifiedWebhook: initialWebhook,
|
||||
triggerResponse: {},
|
||||
initialData: initialWebhook,
|
||||
isTriggering: false,
|
||||
modifiedData: initialWebhook,
|
||||
triggerResponse: {},
|
||||
});
|
||||
|
||||
const reducer = (state, action) => {
|
||||
switch (action.type) {
|
||||
case 'ADD_NEW_HEADER':
|
||||
return state.updateIn(['modifiedData', ...action.keys], arr =>
|
||||
arr.push(fromJS(header))
|
||||
);
|
||||
case 'GET_DATA_SUCCEEDED': {
|
||||
const data = cloneDeep(action.data);
|
||||
const headers = get(data, 'headers');
|
||||
const headers = get(action, ['data', 'headers'], {});
|
||||
let formattedHeaders = [header];
|
||||
|
||||
if (Object.keys(headers).length > 0) {
|
||||
const newHeaders = fromJS(
|
||||
Object.keys(headers).map(key => {
|
||||
return { key: key, value: headers[key] };
|
||||
})
|
||||
);
|
||||
set(data, ['headers'], newHeaders);
|
||||
} else {
|
||||
set(data, ['headers'], get(initialWebhook, 'headers'));
|
||||
formattedHeaders = Object.keys(headers).map(key => {
|
||||
return { key: key, value: headers[key] };
|
||||
});
|
||||
}
|
||||
|
||||
const data = fromJS(action.data).update('headers', () =>
|
||||
fromJS(formattedHeaders)
|
||||
);
|
||||
|
||||
return state
|
||||
.update('initialWebhook', () => fromJS(data))
|
||||
.update('modifiedWebhook', () => fromJS(data));
|
||||
.update('initialData', () => data)
|
||||
.update('modifiedData', () => data);
|
||||
}
|
||||
case 'IS_TRIGGERING':
|
||||
return state.update('isTriggering', isTriggering => !isTriggering);
|
||||
case 'ON_CHANGE':
|
||||
return state.updateIn(
|
||||
['modifiedData', ...action.keys],
|
||||
() => action.value
|
||||
);
|
||||
case 'ON_HEADER_REMOVE': {
|
||||
return state.updateIn(['modifiedData', 'headers'], headers => {
|
||||
if (headers.size === 1) {
|
||||
return fromJS([header]);
|
||||
}
|
||||
return headers.remove(action.index);
|
||||
});
|
||||
}
|
||||
case 'ON_TRIGGER_CANCELED':
|
||||
return state
|
||||
.update('isTriggering', () => false)
|
||||
.set('triggerResponse', fromJS({}));
|
||||
case 'RESET_FORM':
|
||||
return state.update('modifiedData', () => state.get('initialData'));
|
||||
case 'SET_ERRORS':
|
||||
return state.update('formErrors', () => fromJS(action.errors));
|
||||
case 'TRIGGER_SUCCEEDED':
|
||||
return state
|
||||
.update('triggerResponse', () => fromJS(action.response))
|
||||
.update('isTriggering', () => false);
|
||||
case 'ON_TRIGGER':
|
||||
return state.update('isTriggering', () => true);
|
||||
case 'ON_TRIGGER_CANCELED':
|
||||
return state
|
||||
.update('isTriggering', () => false)
|
||||
.update('triggerResponse', () => {});
|
||||
case 'ON_CHANGE':
|
||||
return state.updateIn(
|
||||
['modifiedWebhook', ...action.keys],
|
||||
() => action.value
|
||||
);
|
||||
case 'ADD_NEW_HEADER':
|
||||
return state.updateIn(['modifiedWebhook', ...action.keys], arr =>
|
||||
arr.push(fromJS({ key: '', value: '' }))
|
||||
);
|
||||
case 'ON_HEADER_REMOVE': {
|
||||
if (action.event === 'remove') {
|
||||
return state.updateIn(['modifiedWebhook', 'headers'], headers =>
|
||||
headers.splice(action.index, 1)
|
||||
);
|
||||
} else {
|
||||
return state.updateIn(
|
||||
['modifiedWebhook', 'headers', action.index],
|
||||
() => fromJS({ key: '', value: '' })
|
||||
);
|
||||
}
|
||||
}
|
||||
case 'RESET':
|
||||
return state.update('modifiedWebhook', () =>
|
||||
fromJS(state.get('initialWebhook'))
|
||||
);
|
||||
case 'SET_ERRORS':
|
||||
return state.update('formErrors', () => fromJS(action.errors));
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
@ -0,0 +1,4 @@
|
||||
const NAME_REGEX = new RegExp('(^$)|(^[A-Za-z][_0-9A-Za-z ]*$)');
|
||||
const URL_REGEX = new RegExp('(^$)|((https?://.*)(d*)/?(.*))');
|
||||
|
||||
export { NAME_REGEX, URL_REGEX };
|
@ -4,36 +4,24 @@ const form = {
|
||||
label: 'Name',
|
||||
type: 'text',
|
||||
value: '',
|
||||
validations: {
|
||||
required: true,
|
||||
regex: new RegExp('(^$)|(^[A-Za-z][_0-9A-Za-z ]*$)'),
|
||||
},
|
||||
},
|
||||
url: {
|
||||
styleName: 'col-12',
|
||||
label: 'URL',
|
||||
type: 'text',
|
||||
value: '',
|
||||
validations: {
|
||||
required: true,
|
||||
regex: new RegExp('(^$)|((https?://.*)(d*)/?(.*))'),
|
||||
},
|
||||
},
|
||||
headers: {
|
||||
styleName: 'col-12',
|
||||
label: 'Headers',
|
||||
type: 'headers',
|
||||
value: [{ key: '', value: '' }],
|
||||
validations: {},
|
||||
},
|
||||
events: {
|
||||
styleName: 'col-12',
|
||||
label: 'Hooks',
|
||||
type: 'events',
|
||||
value: [],
|
||||
validations: {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,40 @@
|
||||
import { set, setWith } from 'lodash';
|
||||
|
||||
const cleanData = data => {
|
||||
const webhooks = data;
|
||||
const headers = cleanHeaders(data.headers);
|
||||
|
||||
set(webhooks, 'headers', unformatHeaders(headers));
|
||||
return webhooks;
|
||||
};
|
||||
|
||||
const unformatHeaders = headers => {
|
||||
return headers.reduce((obj, item) => {
|
||||
const { key, value } = item;
|
||||
return {
|
||||
...obj,
|
||||
[key]: value,
|
||||
};
|
||||
}, {});
|
||||
};
|
||||
|
||||
const cleanHeaders = headers => {
|
||||
if (Object.keys(headers).length === 1) {
|
||||
const { key, value } = headers[0];
|
||||
if (key.length === 0 && value.length === 0) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
return headers;
|
||||
};
|
||||
|
||||
const cleanErrors = errors => {
|
||||
return Object.keys(errors).reduce((acc, curr) => {
|
||||
const { id } = errors[curr];
|
||||
setWith(acc, curr, id ? id : errors[curr], Object);
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
};
|
||||
|
||||
export { cleanHeaders, cleanData, cleanErrors };
|
@ -1,54 +1,31 @@
|
||||
import * as yup from 'yup';
|
||||
import { translatedErrors } from 'strapi-helper-plugin';
|
||||
import { NAME_REGEX, URL_REGEX } from './fieldsRegex';
|
||||
|
||||
const createYupSchema = form =>
|
||||
yup.object().shape(
|
||||
Object.keys(form).reduce((acc, current) => {
|
||||
const { type, validations } = form[current];
|
||||
acc[current] = createYupSchemaEntry(type, validations);
|
||||
const schema = yup.object().shape({
|
||||
name: yup
|
||||
.string(translatedErrors.string)
|
||||
.nullable()
|
||||
.required(translatedErrors.required)
|
||||
.matches(NAME_REGEX, translatedErrors.regex),
|
||||
url: yup
|
||||
.string(translatedErrors.string)
|
||||
.nullable()
|
||||
.required(translatedErrors.required)
|
||||
.matches(URL_REGEX, translatedErrors.regex),
|
||||
headers: yup
|
||||
.array()
|
||||
.of(
|
||||
yup.object().shape({
|
||||
key: yup.string().required(),
|
||||
value: yup.string().required(),
|
||||
})
|
||||
)
|
||||
.nullable(),
|
||||
events: yup
|
||||
.array()
|
||||
.nullable()
|
||||
.required(translatedErrors.required),
|
||||
});
|
||||
|
||||
return acc;
|
||||
}, {})
|
||||
);
|
||||
|
||||
const createYupSchemaEntry = (type, validations) => {
|
||||
let schema = yup.mixed();
|
||||
|
||||
if (['text'].includes(type)) {
|
||||
schema = yup.string(translatedErrors.string).nullable();
|
||||
}
|
||||
|
||||
if (['headers'].includes(type)) {
|
||||
schema = yup
|
||||
.array()
|
||||
.of(
|
||||
yup.object().shape({
|
||||
key: yup.string().required(),
|
||||
value: yup.string().required(),
|
||||
})
|
||||
)
|
||||
.nullable();
|
||||
}
|
||||
|
||||
if (['events'].includes(type)) {
|
||||
schema = yup.array();
|
||||
}
|
||||
|
||||
Object.keys(validations).forEach(validation => {
|
||||
const validationValue = validations[validation];
|
||||
|
||||
switch (validation) {
|
||||
case 'required':
|
||||
schema = schema.required(translatedErrors.required);
|
||||
break;
|
||||
case 'regex':
|
||||
schema = schema.matches(validationValue, translatedErrors.regex);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
|
||||
return schema;
|
||||
};
|
||||
|
||||
export default createYupSchema;
|
||||
export default schema;
|
||||
|
@ -26,25 +26,19 @@ import reducer, { initialState } from './reducer';
|
||||
|
||||
function ListView() {
|
||||
const { formatMessage } = useGlobalContext();
|
||||
const [webhooksToDelete, setWebhooksToDelete] = useState([]);
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const [reducerState, dispatch] = useReducer(reducer, initialState);
|
||||
const { push } = useHistory();
|
||||
const location = useLocation();
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const { shouldRefetchData, webhooks } = reducerState.toJS();
|
||||
const { webhooks, webhooksToDelete } = reducerState.toJS();
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (shouldRefetchData) {
|
||||
fetchData();
|
||||
}
|
||||
}, [shouldRefetchData]);
|
||||
|
||||
const webhookIndex = id => webhooks.findIndex(webhook => webhook.id === id);
|
||||
const getWebhookIndex = id =>
|
||||
webhooks.findIndex(webhook => webhook.id === id);
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
@ -70,7 +64,7 @@ function ListView() {
|
||||
|
||||
const newButtonProps = {
|
||||
label: addBtnLabel,
|
||||
onClick: () => handleCreateClick(),
|
||||
onClick: () => handleGoTo('create'),
|
||||
color: 'primary',
|
||||
type: 'button',
|
||||
icon: <Plus fill="#007eff" width="11px" height="11px" />,
|
||||
@ -119,32 +113,27 @@ function ListView() {
|
||||
items: webhooks,
|
||||
};
|
||||
|
||||
const handleCheckChange = (value, id) => {
|
||||
if (value && !webhooksToDelete.includes(id)) {
|
||||
setWebhooksToDelete([...webhooksToDelete, id]);
|
||||
}
|
||||
const handleChange = (value, id) => {
|
||||
const updatedWebhooksToDelete = value
|
||||
? [...webhooksToDelete, id]
|
||||
: webhooksToDelete.filter(webhookId => webhookId !== id);
|
||||
|
||||
if (!value && webhooksToDelete.includes(id)) {
|
||||
setWebhooksToDelete([
|
||||
...webhooksToDelete.filter(webhookId => webhookId !== id),
|
||||
]);
|
||||
}
|
||||
dispatch({
|
||||
type: 'SET_WEBHOOKS_TO_DELETE',
|
||||
webhooks: updatedWebhooksToDelete,
|
||||
});
|
||||
};
|
||||
|
||||
const handleCreateClick = () => {
|
||||
push(`${location.pathname}/create`);
|
||||
const handleGoTo = to => {
|
||||
push(`${pathname}/${to}`);
|
||||
};
|
||||
|
||||
const handleEditClick = id => {
|
||||
push(`${location.pathname}/${id}`);
|
||||
};
|
||||
|
||||
const handleDeleteAllConfirm = () => {
|
||||
handleDeleteAllClick();
|
||||
const handleDeleteAllConfirm = async () => {
|
||||
await onDeleteAllCLick();
|
||||
setShowModal(false);
|
||||
};
|
||||
|
||||
const handleDeleteAllClick = async () => {
|
||||
const onDeleteAllCLick = async () => {
|
||||
const body = {
|
||||
ids: webhooksToDelete,
|
||||
};
|
||||
@ -158,8 +147,6 @@ function ListView() {
|
||||
dispatch({
|
||||
type: 'WEBHOOKS_DELETED',
|
||||
});
|
||||
|
||||
setWebhooksToDelete([]);
|
||||
} catch (err) {
|
||||
if (err.code !== 20) {
|
||||
strapi.notification.error('notification.error');
|
||||
@ -167,11 +154,12 @@ function ListView() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteClick = id => {
|
||||
deleteWebhook(id);
|
||||
const handleDeleteConfirm = async id => {
|
||||
await onDeleteCLick(id);
|
||||
setShowModal(false);
|
||||
};
|
||||
|
||||
const deleteWebhook = async id => {
|
||||
const onDeleteCLick = async id => {
|
||||
try {
|
||||
await request(`/admin/webhooks/${id}`, {
|
||||
method: 'DELETE',
|
||||
@ -179,7 +167,7 @@ function ListView() {
|
||||
|
||||
dispatch({
|
||||
type: 'WEBHOOK_DELETED',
|
||||
index: webhookIndex(id),
|
||||
index: getWebhookIndex(id),
|
||||
});
|
||||
} catch (err) {
|
||||
if (err.code !== 20) {
|
||||
@ -189,7 +177,10 @@ function ListView() {
|
||||
};
|
||||
|
||||
const handleEnabledChange = async (value, id) => {
|
||||
const initialWebhookProps = webhooks[webhookIndex(id)];
|
||||
const webhookIndex = getWebhookIndex(id);
|
||||
|
||||
const initialWebhookProps = webhooks[webhookIndex];
|
||||
const keys = [webhookIndex, 'isEnabled'];
|
||||
|
||||
const body = {
|
||||
...initialWebhookProps,
|
||||
@ -199,17 +190,23 @@ function ListView() {
|
||||
delete body.id;
|
||||
|
||||
try {
|
||||
dispatch({
|
||||
type: 'SET_WEBHOOK_ENABLED',
|
||||
keys,
|
||||
value: value,
|
||||
});
|
||||
|
||||
await request(`/admin/webhooks/${id}`, {
|
||||
method: 'PUT',
|
||||
body,
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
dispatch({
|
||||
type: 'SET_WEBHOOK_ENABLED',
|
||||
keys: [webhookIndex(id), 'isEnabled'],
|
||||
value: value,
|
||||
keys,
|
||||
value: !value,
|
||||
});
|
||||
} catch (err) {
|
||||
|
||||
if (err.code !== 20) {
|
||||
strapi.notification.error('notification.error');
|
||||
}
|
||||
@ -227,9 +224,9 @@ function ListView() {
|
||||
return (
|
||||
<ListRow
|
||||
{...props}
|
||||
onCheckChange={handleCheckChange}
|
||||
onEditClick={handleEditClick}
|
||||
onDeleteCLick={handleDeleteClick}
|
||||
onCheckChange={handleChange}
|
||||
onEditClick={handleGoTo}
|
||||
onDeleteCLick={handleDeleteConfirm}
|
||||
onEnabledChange={handleEnabledChange}
|
||||
itemsToDelete={webhooksToDelete}
|
||||
/>
|
||||
|
@ -2,23 +2,29 @@ import { fromJS } from 'immutable';
|
||||
|
||||
const initialState = fromJS({
|
||||
webhooks: [],
|
||||
shouldRefetchData: false,
|
||||
webhooksToDelete: [],
|
||||
});
|
||||
|
||||
const reducer = (state, action) => {
|
||||
switch (action.type) {
|
||||
case 'GET_DATA_SUCCEEDED':
|
||||
return state
|
||||
.update('webhooks', () => fromJS(action.data))
|
||||
.update('shouldRefetchData', () => false);
|
||||
return state.update('webhooks', () => fromJS(action.data));
|
||||
case 'SET_WEBHOOK_ENABLED':
|
||||
return state.updateIn(['webhooks', ...action.keys], () => action.value);
|
||||
case 'SET_WEBHOOKS_TO_DELETE':
|
||||
return state.update('webhooksToDelete', () => action.webhooks);
|
||||
case 'WEBHOOKS_DELETED':
|
||||
return state
|
||||
.update('webhooks', webhooks =>
|
||||
webhooks.filter(webhook => {
|
||||
return !state.get('webhooksToDelete').includes(webhook.get('id'));
|
||||
})
|
||||
)
|
||||
.update('webhooksToDelete', () => []);
|
||||
case 'WEBHOOK_DELETED':
|
||||
return state.update('webhooks', webhooks =>
|
||||
webhooks.splice(action.index, 1)
|
||||
);
|
||||
case 'WEBHOOKS_DELETED':
|
||||
return state.update('shouldRefetchData', v => !v);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
@ -221,6 +221,7 @@
|
||||
"Auth.link.forgot-password": "Forgot your password?",
|
||||
"Auth.link.ready": "Ready to sign in?",
|
||||
"Settings.global": "Global Settings",
|
||||
"Settings.error": "Error",
|
||||
"Settings.webhooks.title": "Webhooks",
|
||||
"Settings.webhooks.singular": "webhook",
|
||||
"Settings.webhooks.list.description": "Get POST changes notifications.",
|
||||
@ -242,6 +243,7 @@
|
||||
"Settings.webhooks.trigger.success": "Success!",
|
||||
"Settings.webhooks.trigger.success.label": "Trigger succeded",
|
||||
"Settings.webhooks.trigger.save": "Please save to trigger",
|
||||
"Settings.webhooks.trigger.test": "Test-trigger",
|
||||
"Settings.webhooks.events.create": "Create",
|
||||
"Settings.webhooks.events.edit": "Edit",
|
||||
"Settings.webhooks.events.delete": "Delete",
|
||||
|
@ -1,26 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="23px" height="32px" viewBox="0 0 23 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>Group</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Inputs/Select-(col-6)" transform="translate(-335.000000, -1.000000)">
|
||||
<g id="input">
|
||||
<g id="Number" transform="translate(335.017699, 1.000000)">
|
||||
<g id="Group">
|
||||
<path d="M-5.68434189e-14,0 L20,0 L20,0 C21.1045695,-2.02906125e-16 22,0.8954305 22,2 L22,30 L22,30 C22,31.1045695 21.1045695,32 20,32 L-5.68434189e-14,32 L-5.68434189e-14,0 Z" id="Rectangle" fill="#FAFAFB"></path>
|
||||
<g id="Carets" transform="translate(8.000000, 11.000000)" fill-rule="nonzero" fill="#B3B5B9">
|
||||
<g id="caret-down" transform="translate(0.000000, 7.000000)">
|
||||
<path d="M6,0.375 C6,0.4765625 5.96289062,0.564453125 5.88867188,0.638671875 L3.26367188,3.26367188 C3.18945312,3.33789063 3.1015625,3.375 3,3.375 C2.8984375,3.375 2.81054688,3.33789063 2.73632812,3.26367188 L0.111328125,0.638671875 C0.037109375,0.564453125 0,0.4765625 0,0.375 C0,0.2734375 0.037109375,0.185546875 0.111328125,0.111328125 C0.185546875,0.037109375 0.2734375,0 0.375,0 L5.625,0 C5.7265625,0 5.81445312,0.037109375 5.88867188,0.111328125 C5.96289062,0.185546875 6,0.2734375 6,0.375 Z" id="Shape"></path>
|
||||
</g>
|
||||
<g id="caret-top" transform="translate(3.000000, 2.375000) rotate(180.000000) translate(-3.000000, -2.375000) translate(0.000000, 0.375000)">
|
||||
<path d="M6,0.375 C6,0.4765625 5.96289062,0.564453125 5.88867187,0.638671875 L3.26367187,3.26367188 C3.18945312,3.33789063 3.1015625,3.375 3,3.375 C2.8984375,3.375 2.81054687,3.33789063 2.73632812,3.26367188 L0.111328125,0.638671875 C0.037109375,0.564453125 -1.77635684e-15,0.4765625 -1.77635684e-15,0.375 C-1.77635684e-15,0.2734375 0.037109375,0.185546875 0.111328125,0.111328125 C0.185546875,0.037109375 0.2734375,1.77635684e-15 0.375,1.77635684e-15 L5.625,1.77635684e-15 C5.7265625,1.77635684e-15 5.81445312,0.037109375 5.88867187,0.111328125 C5.96289062,0.185546875 6,0.2734375 6,0.375 Z" id="Shape"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
<svg width="23" height="32" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path d="M.018 0h20a2 2 0 012 2v28a2 2 0 01-2 2h-20V0z" fill="#FAFAFB"/><g fill-rule="nonzero" fill="#B3B5B9"><path d="M14.018 18.375a.36.36 0 01-.112.264l-2.625 2.625a.36.36 0 01-.263.111.36.36 0 01-.264-.111l-2.625-2.625a.36.36 0 01-.111-.264.36.36 0 01.111-.264.36.36 0 01.264-.111h5.25a.36.36 0 01.263.111.36.36 0 01.112.264zM8.018 15a.36.36 0 01.111-.264l2.625-2.625a.36.36 0 01.264-.111.36.36 0 01.263.111l2.625 2.625a.36.36 0 01.112.264.36.36 0 01-.112.264.36.36 0 01-.263.111h-5.25a.36.36 0 01-.264-.111.36.36 0 01-.111-.264z"/></g></g></svg>
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 648 B |
Loading…
x
Reference in New Issue
Block a user