mirror of
https://github.com/strapi/strapi.git
synced 2025-09-26 17:00:55 +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;
|
line-height: normal;
|
||||||
}
|
}
|
||||||
p {
|
p {
|
||||||
line-height: normal;
|
|
||||||
&:first-of-type {
|
&:first-of-type {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
@ -5,13 +5,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
import { sizes } from '@buffetjs/styles';
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
padding-top: 3px;
|
padding-top: 3px;
|
||||||
padding-bottom: 8px;
|
padding-bottom: 8px;
|
||||||
table {
|
table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-radius: 3px;
|
border-radius: ${sizes.borderRadius};
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
tr {
|
tr {
|
||||||
@ -45,8 +46,8 @@ const Wrapper = styled.div`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
tbody {
|
tbody {
|
||||||
border-bottom-left-radius: 3px;
|
border-bottom-left-radius: ${sizes.borderRadius};
|
||||||
border-bottom-right-radius: 3px;
|
border-bottom-right-radius: ${sizes.borderRadius};
|
||||||
box-shadow: inset 0px 0px 0px 1px #f6f6f6;
|
box-shadow: inset 0px 0px 0px 1px #f6f6f6;
|
||||||
td {
|
td {
|
||||||
height: 54px;
|
height: 54px;
|
||||||
|
@ -5,13 +5,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
import { sizes } from '@buffetjs/styles';
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
padding: 23px 24px 26px 24px;
|
padding: 23px 24px 26px 24px;
|
||||||
background-color: #fafafb;
|
background-color: #fafafb;
|
||||||
border-radius: 3px;
|
border-radius: ${sizes.borderRadius};
|
||||||
ul {
|
> ul {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
@ -29,67 +30,68 @@ const Wrapper = styled.div`
|
|||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
li {
|
||||||
li {
|
position: relative;
|
||||||
position: relative;
|
padding-right: 30px;
|
||||||
padding-right: 30px;
|
&:not(:first-of-type) {
|
||||||
&:not(:first-of-type) {
|
margin-bottom: 20px;
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
&:last-of-type {
|
|
||||||
margin-bottom: 6px;
|
|
||||||
}
|
|
||||||
> section {
|
|
||||||
display: inline-block;
|
|
||||||
width: 50%;
|
|
||||||
vertical-align: top;
|
|
||||||
&:nth-child(odd) {
|
|
||||||
padding-right: 15px;
|
|
||||||
}
|
}
|
||||||
&:nth-child(even) {
|
&:last-of-type {
|
||||||
padding-left: 15px;
|
margin-bottom: 6px;
|
||||||
}
|
}
|
||||||
> p {
|
> section {
|
||||||
font-size: 13px;
|
display: inline-block;
|
||||||
color: #333740;
|
width: 50%;
|
||||||
font-weight: 500;
|
vertical-align: top;
|
||||||
}
|
&:nth-child(odd) {
|
||||||
> div:first-of-type {
|
padding-right: 15px;
|
||||||
height: 34px;
|
}
|
||||||
> div:first-of-type {
|
&:nth-child(even) {
|
||||||
height: 34px;
|
padding-left: 15px;
|
||||||
min-height: 34px;
|
}
|
||||||
border: 1px solid #e3e9f3;
|
> p {
|
||||||
border-radius: 2px;
|
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: #333740;
|
color: #333740;
|
||||||
align-items: normal;
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
> div:first-of-type {
|
||||||
|
height: 34px;
|
||||||
> div:first-of-type {
|
> div:first-of-type {
|
||||||
height: 32px;
|
height: 34px;
|
||||||
padding: 0 1rem;
|
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 {
|
> span + div:first-of-type {
|
||||||
display: none;
|
border-color: #78caff;
|
||||||
}
|
box-shadow: none;
|
||||||
&:hover {
|
|
||||||
cursor: text;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
> span + div:first-of-type {
|
& + div {
|
||||||
border-color: #78caff;
|
position: absolute;
|
||||||
box-shadow: none;
|
top: 7px;
|
||||||
}
|
right: 0;
|
||||||
}
|
button {
|
||||||
& + div {
|
margin: 0;
|
||||||
position: absolute;
|
}
|
||||||
top: 7px;
|
|
||||||
right: 0;
|
|
||||||
button {
|
|
||||||
margin: 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.bordered {
|
.bordered {
|
||||||
input {
|
input {
|
||||||
border: 1px solid #f64d0a;
|
border: 1px solid #f64d0a;
|
||||||
|
@ -7,6 +7,7 @@ import { CircleButton } from 'strapi-helper-plugin';
|
|||||||
import { InputText } from '@buffetjs/core';
|
import { InputText } from '@buffetjs/core';
|
||||||
import { Plus } from '@buffetjs/icons';
|
import { Plus } from '@buffetjs/icons';
|
||||||
|
|
||||||
|
import borderColor from './utils/borderColor';
|
||||||
import keys from './keys';
|
import keys from './keys';
|
||||||
import Wrapper from './Wrapper';
|
import Wrapper from './Wrapper';
|
||||||
|
|
||||||
@ -19,8 +20,8 @@ const HeadersInput = ({
|
|||||||
onRemove,
|
onRemove,
|
||||||
value,
|
value,
|
||||||
}) => {
|
}) => {
|
||||||
const optionFormat = value => ({ value: value, label: value });
|
const formatOption = value => ({ value: value, label: value });
|
||||||
const options = keys.map(key => optionFormat(key));
|
const options = keys.map(key => formatOption(key));
|
||||||
|
|
||||||
const handleBlur = () => onBlur({ target: { name, value } });
|
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 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 {
|
return {
|
||||||
control: (base, state) => ({
|
control: (base, state) => ({
|
||||||
...base,
|
...base,
|
||||||
border: selectBorder(state.isFocused),
|
border: `1px solid ${borderColor({
|
||||||
|
isFocused: state.isFocused,
|
||||||
|
hasError,
|
||||||
|
})} !important`,
|
||||||
borderRadius: '2px !important',
|
borderRadius: '2px !important',
|
||||||
}),
|
}),
|
||||||
menu: base => {
|
menu: base => {
|
||||||
@ -121,7 +108,7 @@ const HeadersInput = ({
|
|||||||
name={`${name}.${index}.key`}
|
name={`${name}.${index}.key`}
|
||||||
options={options}
|
options={options}
|
||||||
styles={customStyles(entryErrors && entryErrors.key)}
|
styles={customStyles(entryErrors && entryErrors.key)}
|
||||||
value={optionFormat(key)}
|
value={formatOption(key)}
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
@ -137,14 +124,14 @@ const HeadersInput = ({
|
|||||||
<CircleButton
|
<CircleButton
|
||||||
type="button"
|
type="button"
|
||||||
isRemoveButton
|
isRemoveButton
|
||||||
onClick={() => handleRemoveItem(index)}
|
onClick={() => onRemove(index)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
<button onClick={handleClick} type="button">
|
<button onClick={() => onClick(name)} type="button">
|
||||||
<Plus fill="#007eff" width="10px" />
|
<Plus fill="#007eff" width="10px" />
|
||||||
<FormattedMessage id="Settings.webhooks.create.header" />
|
<FormattedMessage id="Settings.webhooks.create.header" />
|
||||||
</button>
|
</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 PropTypes from 'prop-types';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import { ErrorMessage, Label } from '@buffetjs/styles';
|
import { ErrorMessage, Label } from '@buffetjs/styles';
|
||||||
import { Error, InputText } from '@buffetjs/core';
|
import { Error } from '@buffetjs/core';
|
||||||
|
|
||||||
import HeadersInput from '../HeadersInput';
|
import HeadersInput from '../HeadersInput';
|
||||||
import EventInput from '../EventInput';
|
import EventInput from '../EventInput';
|
||||||
@ -71,29 +71,15 @@ function Inputs({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{type === 'events' ? (
|
<EventInput
|
||||||
<EventInput
|
name={name}
|
||||||
name={name}
|
onChange={e => {
|
||||||
onChange={e => {
|
handleChange(e);
|
||||||
handleChange(e);
|
onBlur(e);
|
||||||
onBlur(e);
|
}}
|
||||||
}}
|
value={value}
|
||||||
value={value}
|
/>
|
||||||
/>
|
{hasError && <ErrorMessage>{error}</ErrorMessage>}
|
||||||
) : (
|
|
||||||
<InputText
|
|
||||||
className={hasError ? 'hasError' : ''}
|
|
||||||
name={name}
|
|
||||||
onBlur={onBlur}
|
|
||||||
onChange={handleChange}
|
|
||||||
value={value}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{hasError && (
|
|
||||||
<ErrorMessage>
|
|
||||||
<FormattedMessage id={error} />
|
|
||||||
</ErrorMessage>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
@ -28,7 +28,7 @@ function ListRow({
|
|||||||
const links = [
|
const links = [
|
||||||
{
|
{
|
||||||
icon: 'pencil',
|
icon: 'pencil',
|
||||||
onClick: () => handleEditClick(),
|
onClick: () => onEditClick(id),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'trash',
|
icon: 'trash',
|
||||||
@ -41,26 +41,19 @@ function ListRow({
|
|||||||
|
|
||||||
const isChecked = itemsToDelete.includes(id);
|
const isChecked = itemsToDelete.includes(id);
|
||||||
|
|
||||||
const handleEditClick = () => onEditClick(id);
|
const handleDeleteConfirm = async () => {
|
||||||
|
await onDeleteCLick(id);
|
||||||
const handleEnabledChange = ({ target: { value } }) =>
|
|
||||||
onEnabledChange(value, id);
|
|
||||||
|
|
||||||
const handleCheckChange = ({ target: { value } }) => onCheckChange(value, id);
|
|
||||||
|
|
||||||
const handleDeleteConfirm = () => {
|
|
||||||
onDeleteCLick(id);
|
|
||||||
setShowModal(false);
|
setShowModal(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledListRow onClick={handleEditClick}>
|
<StyledListRow onClick={() => onEditClick(id)}>
|
||||||
<td>
|
<td>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
name={name}
|
name={name}
|
||||||
value={isChecked}
|
value={isChecked}
|
||||||
onClick={e => e.stopPropagation()}
|
onClick={e => e.stopPropagation()}
|
||||||
onChange={handleCheckChange}
|
onChange={({ target: { value } }) => onCheckChange(value, id)}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@ -74,7 +67,7 @@ function ListRow({
|
|||||||
<Switch
|
<Switch
|
||||||
name={name}
|
name={name}
|
||||||
value={isEnabled}
|
value={isEnabled}
|
||||||
onChange={handleEnabledChange}
|
onChange={({ target: { value } }) => onEnabledChange(value, id)}
|
||||||
></Switch>
|
></Switch>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
@ -15,13 +15,17 @@ const TriggerContainer = ({ isPending, onCancel, response }) => {
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<p>Test-trigger</p>
|
<p>
|
||||||
|
{formatMessage({
|
||||||
|
id: `Settings.webhooks.trigger.test`,
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
</td>
|
</td>
|
||||||
{isPending && (
|
{isPending && (
|
||||||
<>
|
<>
|
||||||
<td>
|
<td>
|
||||||
<p>
|
<p>
|
||||||
<Pending fill="#6DBB1A" width="15px" height="15px" />
|
<Pending fill="#ffb500" width="15px" height="15px" />
|
||||||
<span>
|
<span>
|
||||||
{formatMessage({
|
{formatMessage({
|
||||||
id: `Settings.webhooks.trigger.pending`,
|
id: `Settings.webhooks.trigger.pending`,
|
||||||
@ -65,7 +69,12 @@ const TriggerContainer = ({ isPending, onCancel, response }) => {
|
|||||||
<td>
|
<td>
|
||||||
<p className="fail-label">
|
<p className="fail-label">
|
||||||
<Fail fill="#f64d0a" width="15px" height="15px" />
|
<Fail fill="#f64d0a" width="15px" height="15px" />
|
||||||
<span>Error {statusCode}</span>
|
<span>
|
||||||
|
{formatMessage({
|
||||||
|
id: `Settings.error`,
|
||||||
|
})}{' '}
|
||||||
|
{statusCode}
|
||||||
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</td>
|
</td>
|
||||||
<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 => {
|
renderPluginDispatcher = props => {
|
||||||
// NOTE: Send the needed props instead of everything...
|
// NOTE: Send the needed props instead of everything...
|
||||||
|
|
||||||
@ -183,10 +179,12 @@ export class Admin extends React.Component {
|
|||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="/marketplace"
|
path="/marketplace"
|
||||||
render={this.renderMarketPlace}
|
render={props => this.renderRoute(props, Marketplace)}
|
||||||
exact
|
/>
|
||||||
|
<Route
|
||||||
|
path="/settings"
|
||||||
|
render={props => this.renderRoute(props, SettingsPage)}
|
||||||
/>
|
/>
|
||||||
<Route path="/settings" render={this.renderSettings} />
|
|
||||||
<Route key="7" path="" component={NotFoundPage} />
|
<Route key="7" path="" component={NotFoundPage} />
|
||||||
<Route key="8" path="404" component={NotFoundPage} />
|
<Route key="8" path="404" component={NotFoundPage} />
|
||||||
</Switch>
|
</Switch>
|
||||||
|
@ -32,9 +32,7 @@ function SettingsPage() {
|
|||||||
<div className="col-md-3">
|
<div className="col-md-3">
|
||||||
<LeftMenu>
|
<LeftMenu>
|
||||||
{menuItems.map(item => {
|
{menuItems.map(item => {
|
||||||
return (
|
return <LeftMenuList {...item} key={item.title.id} />;
|
||||||
<LeftMenuList {...item} key={JSON.stringify(item.title)} />
|
|
||||||
);
|
|
||||||
})}
|
})}
|
||||||
</LeftMenu>
|
</LeftMenu>
|
||||||
</div>
|
</div>
|
||||||
|
@ -32,6 +32,8 @@ const Wrapper = styled.div`
|
|||||||
}
|
}
|
||||||
span svg {
|
span svg {
|
||||||
margin-top: -2px;
|
margin-top: -2px;
|
||||||
|
margin-right: -10px;
|
||||||
|
margin-bottom: -5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useEffect, useReducer, useCallback, useState } from 'react';
|
import React, { useEffect, useReducer, useCallback, useState } from 'react';
|
||||||
import { useHistory, useLocation } from 'react-router-dom';
|
import { useHistory, useParams } from 'react-router-dom';
|
||||||
import { get, isEmpty, isEqual, set, setWith } from 'lodash';
|
import { get, isEmpty, isEqual, set } from 'lodash';
|
||||||
import { Header } from '@buffetjs/custom';
|
import { Header, Inputs as InputsIndex } from '@buffetjs/custom';
|
||||||
import { Play } from '@buffetjs/icons';
|
import { Play } from '@buffetjs/icons';
|
||||||
import {
|
import {
|
||||||
request,
|
request,
|
||||||
@ -21,7 +21,8 @@ import TriggerContainer from '../../../components/TriggerContainer';
|
|||||||
|
|
||||||
import reducer, { initialState } from './reducer';
|
import reducer, { initialState } from './reducer';
|
||||||
import form from './utils/form';
|
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';
|
import Wrapper from './Wrapper';
|
||||||
|
|
||||||
@ -29,31 +30,105 @@ function EditView() {
|
|||||||
const { formatMessage } = useGlobalContext();
|
const { formatMessage } = useGlobalContext();
|
||||||
const [submittedOnce, setSubmittedOnce] = useState(false);
|
const [submittedOnce, setSubmittedOnce] = useState(false);
|
||||||
const [reducerState, dispatch] = useReducer(reducer, initialState);
|
const [reducerState, dispatch] = useReducer(reducer, initialState);
|
||||||
const location = useLocation();
|
|
||||||
const { push } = useHistory();
|
const { push } = useHistory();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
formErrors,
|
formErrors,
|
||||||
modifiedWebhook,
|
modifiedData,
|
||||||
initialWebhook,
|
initialData,
|
||||||
isTriggering,
|
isTriggering,
|
||||||
triggerResponse,
|
triggerResponse,
|
||||||
} = reducerState.toJS();
|
} = reducerState.toJS();
|
||||||
|
|
||||||
const { name } = modifiedWebhook;
|
const { name } = modifiedData;
|
||||||
|
const { id } = useParams();
|
||||||
const id = location.pathname.split('/')[3];
|
|
||||||
const isCreating = id === 'create';
|
const isCreating = id === 'create';
|
||||||
|
|
||||||
const abortController = new AbortController();
|
const abortController = new AbortController();
|
||||||
|
|
||||||
const { signal } = abortController;
|
const { signal } = abortController;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isCreating) {
|
if (!isCreating) {
|
||||||
fetchData();
|
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 () => {
|
const fetchData = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
@ -72,115 +147,57 @@ function EditView() {
|
|||||||
}
|
}
|
||||||
}, [id]);
|
}, [id]);
|
||||||
|
|
||||||
const headerTitle = isCreating
|
const submitForm = () => {
|
||||||
? formatMessage({
|
if (!isCreating) {
|
||||||
id: `Settings.webhooks.create`,
|
updateWebhook();
|
||||||
})
|
} else {
|
||||||
: name;
|
createWebhooks();
|
||||||
|
|
||||||
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 onCancelTrigger = () => {
|
const createWebhooks = async () => {
|
||||||
abortController.abort();
|
try {
|
||||||
|
await request(`/admin/webhooks`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: cleanData(modifiedData),
|
||||||
|
});
|
||||||
|
|
||||||
dispatch({
|
strapi.notification.success(`notification.success`);
|
||||||
type: 'ON_TRIGGER_CANCELED',
|
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 = () =>
|
const handleReset = () =>
|
||||||
dispatch({
|
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 = () => {
|
const handleBlur = () => {
|
||||||
if (submittedOnce) checkFormErrors();
|
if (submittedOnce) {
|
||||||
|
checkFormErrors();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChange = ({ target: { name, value } }) => {
|
const handleChange = ({ target: { name, value } }) => {
|
||||||
@ -198,11 +215,10 @@ function EditView() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRemove = ({ event, index }) => {
|
const handleRemove = index => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'ON_HEADER_REMOVE',
|
type: 'ON_HEADER_REMOVE',
|
||||||
index,
|
index,
|
||||||
event,
|
|
||||||
});
|
});
|
||||||
resetError('headers');
|
resetError('headers');
|
||||||
};
|
};
|
||||||
@ -213,20 +229,14 @@ function EditView() {
|
|||||||
checkFormErrors(true);
|
checkFormErrors(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const submitForm = () => {
|
/* Validations */
|
||||||
if (!isCreating) {
|
|
||||||
updateWebhook();
|
|
||||||
} else {
|
|
||||||
createWebhooks();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const checkFormErrors = async (submit = false) => {
|
const checkFormErrors = async (submit = false) => {
|
||||||
const webhookToCheck = modifiedWebhook;
|
const webhookToCheck = modifiedData;
|
||||||
set(webhookToCheck, 'headers', cleanHeaders());
|
set(webhookToCheck, 'headers', cleanHeaders(modifiedData.headers));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await createYupSchema(form).validate(webhookToCheck, {
|
await schema.validate(webhookToCheck, {
|
||||||
abortEarly: false,
|
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 resetError = name => {
|
||||||
const errors = formErrors;
|
const errors = formErrors;
|
||||||
|
|
||||||
@ -260,68 +258,66 @@ function EditView() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const setErrors = errors => {
|
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({
|
dispatch({
|
||||||
type: 'SET_ERRORS',
|
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 {
|
try {
|
||||||
await request(`/admin/webhooks`, {
|
const { data } = await request(`/admin/webhooks/${id}/trigger`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: formatWebhook(),
|
signal,
|
||||||
});
|
});
|
||||||
|
|
||||||
strapi.notification.success(`notification.success`);
|
dispatch({
|
||||||
goBack();
|
type: 'TRIGGER_SUCCEEDED',
|
||||||
|
response: data,
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
strapi.notification.error('notification.error');
|
if (err.code !== 20) {
|
||||||
|
strapi.notification.error('notification.error');
|
||||||
|
}
|
||||||
|
dispatch({
|
||||||
|
type: 'IS_TRIGGERING',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateWebhook = async () => {
|
const onCancelTrigger = () => {
|
||||||
try {
|
abortController.abort();
|
||||||
const body = formatWebhook();
|
|
||||||
delete body.id;
|
|
||||||
|
|
||||||
await request(`/admin/webhooks/${id}`, {
|
dispatch({
|
||||||
method: 'PUT',
|
type: 'ON_TRIGGER_CANCELED',
|
||||||
body,
|
});
|
||||||
});
|
|
||||||
|
|
||||||
fetchData();
|
|
||||||
|
|
||||||
strapi.notification.error('notification.form.success.fields');
|
|
||||||
} catch (err) {
|
|
||||||
strapi.notification.error('notification.error');
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// utils
|
/* Nav */
|
||||||
const formatWebhook = () => {
|
|
||||||
const webhooks = modifiedWebhook;
|
|
||||||
set(webhooks, 'headers', unformatHeaders(cleanHeaders()));
|
|
||||||
return webhooks;
|
|
||||||
};
|
|
||||||
|
|
||||||
const unformatHeaders = headers => {
|
const goBack = () => push('/settings/webhooks');
|
||||||
return headers.reduce((obj, item) => {
|
|
||||||
const { key, value } = item;
|
const renderHeadersInput = () => props => (
|
||||||
return {
|
<Inputs {...props} onClick={handleClick} onRemove={handleRemove} />
|
||||||
...obj,
|
);
|
||||||
[key]: value,
|
|
||||||
};
|
|
||||||
}, {});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
@ -343,16 +339,18 @@ function EditView() {
|
|||||||
{Object.keys(form).map(key => {
|
{Object.keys(form).map(key => {
|
||||||
return (
|
return (
|
||||||
<div key={key} className={form[key].styleName}>
|
<div key={key} className={form[key].styleName}>
|
||||||
<Inputs
|
<InputsIndex
|
||||||
{...form[key]}
|
{...form[key]}
|
||||||
error={get(formErrors, key, null)}
|
customInputs={{
|
||||||
|
headers: renderHeadersInput(),
|
||||||
|
events: Inputs,
|
||||||
|
}}
|
||||||
|
error={errorMessage(get(formErrors, key, null))}
|
||||||
name={key}
|
name={key}
|
||||||
onBlur={handleBlur}
|
onBlur={handleBlur}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
onClick={handleClick}
|
|
||||||
onRemove={handleRemove}
|
|
||||||
validations={form[key].validations}
|
validations={form[key].validations}
|
||||||
value={modifiedWebhook[key] || form[key].value}
|
value={modifiedData[key] || form[key].value}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,79 +1,74 @@
|
|||||||
import { fromJS } from 'immutable';
|
import { fromJS } from 'immutable';
|
||||||
import { cloneDeep, set, get } from 'lodash';
|
import { get } from 'lodash';
|
||||||
|
|
||||||
|
const header = { key: '', value: '' };
|
||||||
|
|
||||||
const initialWebhook = {
|
const initialWebhook = {
|
||||||
|
events: [],
|
||||||
|
headers: [header],
|
||||||
name: null,
|
name: null,
|
||||||
url: null,
|
url: null,
|
||||||
headers: [{ key: '', value: '' }],
|
|
||||||
events: [],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialState = fromJS({
|
const initialState = fromJS({
|
||||||
formErrors: {},
|
formErrors: {},
|
||||||
initialWebhook: initialWebhook,
|
initialData: initialWebhook,
|
||||||
modifiedWebhook: initialWebhook,
|
|
||||||
triggerResponse: {},
|
|
||||||
isTriggering: false,
|
isTriggering: false,
|
||||||
|
modifiedData: initialWebhook,
|
||||||
|
triggerResponse: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
const reducer = (state, action) => {
|
const reducer = (state, action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
case 'ADD_NEW_HEADER':
|
||||||
|
return state.updateIn(['modifiedData', ...action.keys], arr =>
|
||||||
|
arr.push(fromJS(header))
|
||||||
|
);
|
||||||
case 'GET_DATA_SUCCEEDED': {
|
case 'GET_DATA_SUCCEEDED': {
|
||||||
const data = cloneDeep(action.data);
|
const headers = get(action, ['data', 'headers'], {});
|
||||||
const headers = get(data, 'headers');
|
let formattedHeaders = [header];
|
||||||
|
|
||||||
if (Object.keys(headers).length > 0) {
|
if (Object.keys(headers).length > 0) {
|
||||||
const newHeaders = fromJS(
|
formattedHeaders = Object.keys(headers).map(key => {
|
||||||
Object.keys(headers).map(key => {
|
return { key: key, value: headers[key] };
|
||||||
return { key: key, value: headers[key] };
|
});
|
||||||
})
|
|
||||||
);
|
|
||||||
set(data, ['headers'], newHeaders);
|
|
||||||
} else {
|
|
||||||
set(data, ['headers'], get(initialWebhook, 'headers'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const data = fromJS(action.data).update('headers', () =>
|
||||||
|
fromJS(formattedHeaders)
|
||||||
|
);
|
||||||
|
|
||||||
return state
|
return state
|
||||||
.update('initialWebhook', () => fromJS(data))
|
.update('initialData', () => data)
|
||||||
.update('modifiedWebhook', () => fromJS(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':
|
case 'TRIGGER_SUCCEEDED':
|
||||||
return state
|
return state
|
||||||
.update('triggerResponse', () => fromJS(action.response))
|
.update('triggerResponse', () => fromJS(action.response))
|
||||||
.update('isTriggering', () => false);
|
.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:
|
default:
|
||||||
return state;
|
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',
|
label: 'Name',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
value: '',
|
value: '',
|
||||||
validations: {
|
|
||||||
required: true,
|
|
||||||
regex: new RegExp('(^$)|(^[A-Za-z][_0-9A-Za-z ]*$)'),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
url: {
|
url: {
|
||||||
styleName: 'col-12',
|
styleName: 'col-12',
|
||||||
label: 'URL',
|
label: 'URL',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
value: '',
|
value: '',
|
||||||
validations: {
|
|
||||||
required: true,
|
|
||||||
regex: new RegExp('(^$)|((https?://.*)(d*)/?(.*))'),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
styleName: 'col-12',
|
styleName: 'col-12',
|
||||||
label: 'Headers',
|
label: 'Headers',
|
||||||
type: 'headers',
|
type: 'headers',
|
||||||
value: [{ key: '', value: '' }],
|
value: [{ key: '', value: '' }],
|
||||||
validations: {},
|
|
||||||
},
|
},
|
||||||
events: {
|
events: {
|
||||||
styleName: 'col-12',
|
styleName: 'col-12',
|
||||||
label: 'Hooks',
|
label: 'Hooks',
|
||||||
type: 'events',
|
type: 'events',
|
||||||
value: [],
|
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 * as yup from 'yup';
|
||||||
import { translatedErrors } from 'strapi-helper-plugin';
|
import { translatedErrors } from 'strapi-helper-plugin';
|
||||||
|
import { NAME_REGEX, URL_REGEX } from './fieldsRegex';
|
||||||
|
|
||||||
const createYupSchema = form =>
|
const schema = yup.object().shape({
|
||||||
yup.object().shape(
|
name: yup
|
||||||
Object.keys(form).reduce((acc, current) => {
|
.string(translatedErrors.string)
|
||||||
const { type, validations } = form[current];
|
.nullable()
|
||||||
acc[current] = createYupSchemaEntry(type, validations);
|
.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;
|
export default schema;
|
||||||
}, {})
|
|
||||||
);
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
@ -26,25 +26,19 @@ import reducer, { initialState } from './reducer';
|
|||||||
|
|
||||||
function ListView() {
|
function ListView() {
|
||||||
const { formatMessage } = useGlobalContext();
|
const { formatMessage } = useGlobalContext();
|
||||||
const [webhooksToDelete, setWebhooksToDelete] = useState([]);
|
|
||||||
const [showModal, setShowModal] = useState(false);
|
const [showModal, setShowModal] = useState(false);
|
||||||
const [reducerState, dispatch] = useReducer(reducer, initialState);
|
const [reducerState, dispatch] = useReducer(reducer, initialState);
|
||||||
const { push } = useHistory();
|
const { push } = useHistory();
|
||||||
const location = useLocation();
|
const { pathname } = useLocation();
|
||||||
|
|
||||||
const { shouldRefetchData, webhooks } = reducerState.toJS();
|
const { webhooks, webhooksToDelete } = reducerState.toJS();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchData();
|
fetchData();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
const getWebhookIndex = id =>
|
||||||
if (shouldRefetchData) {
|
webhooks.findIndex(webhook => webhook.id === id);
|
||||||
fetchData();
|
|
||||||
}
|
|
||||||
}, [shouldRefetchData]);
|
|
||||||
|
|
||||||
const webhookIndex = id => webhooks.findIndex(webhook => webhook.id === id);
|
|
||||||
|
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
try {
|
try {
|
||||||
@ -70,7 +64,7 @@ function ListView() {
|
|||||||
|
|
||||||
const newButtonProps = {
|
const newButtonProps = {
|
||||||
label: addBtnLabel,
|
label: addBtnLabel,
|
||||||
onClick: () => handleCreateClick(),
|
onClick: () => handleGoTo('create'),
|
||||||
color: 'primary',
|
color: 'primary',
|
||||||
type: 'button',
|
type: 'button',
|
||||||
icon: <Plus fill="#007eff" width="11px" height="11px" />,
|
icon: <Plus fill="#007eff" width="11px" height="11px" />,
|
||||||
@ -119,32 +113,27 @@ function ListView() {
|
|||||||
items: webhooks,
|
items: webhooks,
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCheckChange = (value, id) => {
|
const handleChange = (value, id) => {
|
||||||
if (value && !webhooksToDelete.includes(id)) {
|
const updatedWebhooksToDelete = value
|
||||||
setWebhooksToDelete([...webhooksToDelete, id]);
|
? [...webhooksToDelete, id]
|
||||||
}
|
: webhooksToDelete.filter(webhookId => webhookId !== id);
|
||||||
|
|
||||||
if (!value && webhooksToDelete.includes(id)) {
|
dispatch({
|
||||||
setWebhooksToDelete([
|
type: 'SET_WEBHOOKS_TO_DELETE',
|
||||||
...webhooksToDelete.filter(webhookId => webhookId !== id),
|
webhooks: updatedWebhooksToDelete,
|
||||||
]);
|
});
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCreateClick = () => {
|
const handleGoTo = to => {
|
||||||
push(`${location.pathname}/create`);
|
push(`${pathname}/${to}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEditClick = id => {
|
const handleDeleteAllConfirm = async () => {
|
||||||
push(`${location.pathname}/${id}`);
|
await onDeleteAllCLick();
|
||||||
};
|
|
||||||
|
|
||||||
const handleDeleteAllConfirm = () => {
|
|
||||||
handleDeleteAllClick();
|
|
||||||
setShowModal(false);
|
setShowModal(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDeleteAllClick = async () => {
|
const onDeleteAllCLick = async () => {
|
||||||
const body = {
|
const body = {
|
||||||
ids: webhooksToDelete,
|
ids: webhooksToDelete,
|
||||||
};
|
};
|
||||||
@ -158,8 +147,6 @@ function ListView() {
|
|||||||
dispatch({
|
dispatch({
|
||||||
type: 'WEBHOOKS_DELETED',
|
type: 'WEBHOOKS_DELETED',
|
||||||
});
|
});
|
||||||
|
|
||||||
setWebhooksToDelete([]);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.code !== 20) {
|
if (err.code !== 20) {
|
||||||
strapi.notification.error('notification.error');
|
strapi.notification.error('notification.error');
|
||||||
@ -167,11 +154,12 @@ function ListView() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDeleteClick = id => {
|
const handleDeleteConfirm = async id => {
|
||||||
deleteWebhook(id);
|
await onDeleteCLick(id);
|
||||||
|
setShowModal(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteWebhook = async id => {
|
const onDeleteCLick = async id => {
|
||||||
try {
|
try {
|
||||||
await request(`/admin/webhooks/${id}`, {
|
await request(`/admin/webhooks/${id}`, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
@ -179,7 +167,7 @@ function ListView() {
|
|||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'WEBHOOK_DELETED',
|
type: 'WEBHOOK_DELETED',
|
||||||
index: webhookIndex(id),
|
index: getWebhookIndex(id),
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.code !== 20) {
|
if (err.code !== 20) {
|
||||||
@ -189,7 +177,10 @@ function ListView() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleEnabledChange = async (value, id) => {
|
const handleEnabledChange = async (value, id) => {
|
||||||
const initialWebhookProps = webhooks[webhookIndex(id)];
|
const webhookIndex = getWebhookIndex(id);
|
||||||
|
|
||||||
|
const initialWebhookProps = webhooks[webhookIndex];
|
||||||
|
const keys = [webhookIndex, 'isEnabled'];
|
||||||
|
|
||||||
const body = {
|
const body = {
|
||||||
...initialWebhookProps,
|
...initialWebhookProps,
|
||||||
@ -199,17 +190,23 @@ function ListView() {
|
|||||||
delete body.id;
|
delete body.id;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
dispatch({
|
||||||
|
type: 'SET_WEBHOOK_ENABLED',
|
||||||
|
keys,
|
||||||
|
value: value,
|
||||||
|
});
|
||||||
|
|
||||||
await request(`/admin/webhooks/${id}`, {
|
await request(`/admin/webhooks/${id}`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
body,
|
body,
|
||||||
});
|
});
|
||||||
|
} catch (err) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'SET_WEBHOOK_ENABLED',
|
type: 'SET_WEBHOOK_ENABLED',
|
||||||
keys: [webhookIndex(id), 'isEnabled'],
|
keys,
|
||||||
value: value,
|
value: !value,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
|
||||||
if (err.code !== 20) {
|
if (err.code !== 20) {
|
||||||
strapi.notification.error('notification.error');
|
strapi.notification.error('notification.error');
|
||||||
}
|
}
|
||||||
@ -227,9 +224,9 @@ function ListView() {
|
|||||||
return (
|
return (
|
||||||
<ListRow
|
<ListRow
|
||||||
{...props}
|
{...props}
|
||||||
onCheckChange={handleCheckChange}
|
onCheckChange={handleChange}
|
||||||
onEditClick={handleEditClick}
|
onEditClick={handleGoTo}
|
||||||
onDeleteCLick={handleDeleteClick}
|
onDeleteCLick={handleDeleteConfirm}
|
||||||
onEnabledChange={handleEnabledChange}
|
onEnabledChange={handleEnabledChange}
|
||||||
itemsToDelete={webhooksToDelete}
|
itemsToDelete={webhooksToDelete}
|
||||||
/>
|
/>
|
||||||
|
@ -2,23 +2,29 @@ import { fromJS } from 'immutable';
|
|||||||
|
|
||||||
const initialState = fromJS({
|
const initialState = fromJS({
|
||||||
webhooks: [],
|
webhooks: [],
|
||||||
shouldRefetchData: false,
|
webhooksToDelete: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const reducer = (state, action) => {
|
const reducer = (state, action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'GET_DATA_SUCCEEDED':
|
case 'GET_DATA_SUCCEEDED':
|
||||||
return state
|
return state.update('webhooks', () => fromJS(action.data));
|
||||||
.update('webhooks', () => fromJS(action.data))
|
|
||||||
.update('shouldRefetchData', () => false);
|
|
||||||
case 'SET_WEBHOOK_ENABLED':
|
case 'SET_WEBHOOK_ENABLED':
|
||||||
return state.updateIn(['webhooks', ...action.keys], () => action.value);
|
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':
|
case 'WEBHOOK_DELETED':
|
||||||
return state.update('webhooks', webhooks =>
|
return state.update('webhooks', webhooks =>
|
||||||
webhooks.splice(action.index, 1)
|
webhooks.splice(action.index, 1)
|
||||||
);
|
);
|
||||||
case 'WEBHOOKS_DELETED':
|
|
||||||
return state.update('shouldRefetchData', v => !v);
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -221,6 +221,7 @@
|
|||||||
"Auth.link.forgot-password": "Forgot your password?",
|
"Auth.link.forgot-password": "Forgot your password?",
|
||||||
"Auth.link.ready": "Ready to sign in?",
|
"Auth.link.ready": "Ready to sign in?",
|
||||||
"Settings.global": "Global Settings",
|
"Settings.global": "Global Settings",
|
||||||
|
"Settings.error": "Error",
|
||||||
"Settings.webhooks.title": "Webhooks",
|
"Settings.webhooks.title": "Webhooks",
|
||||||
"Settings.webhooks.singular": "webhook",
|
"Settings.webhooks.singular": "webhook",
|
||||||
"Settings.webhooks.list.description": "Get POST changes notifications.",
|
"Settings.webhooks.list.description": "Get POST changes notifications.",
|
||||||
@ -242,6 +243,7 @@
|
|||||||
"Settings.webhooks.trigger.success": "Success!",
|
"Settings.webhooks.trigger.success": "Success!",
|
||||||
"Settings.webhooks.trigger.success.label": "Trigger succeded",
|
"Settings.webhooks.trigger.success.label": "Trigger succeded",
|
||||||
"Settings.webhooks.trigger.save": "Please save to trigger",
|
"Settings.webhooks.trigger.save": "Please save to trigger",
|
||||||
|
"Settings.webhooks.trigger.test": "Test-trigger",
|
||||||
"Settings.webhooks.events.create": "Create",
|
"Settings.webhooks.events.create": "Create",
|
||||||
"Settings.webhooks.events.edit": "Edit",
|
"Settings.webhooks.events.edit": "Edit",
|
||||||
"Settings.webhooks.events.delete": "Delete",
|
"Settings.webhooks.events.delete": "Delete",
|
||||||
|
@ -1,26 +1 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<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>
|
||||||
<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>
|
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 648 B |
Loading…
x
Reference in New Issue
Block a user