Add webhook delete

This commit is contained in:
Krzysztof Bialoglowicz 2019-10-10 07:38:21 +02:00
parent 2fd01ec3c5
commit 834addf3c0
14 changed files with 358 additions and 131 deletions

View file

@ -12,14 +12,13 @@ import { UserError } from "@saleor/types";
import React from "react";
import { useIntl } from "react-intl";
import { ServiceList_serviceAccounts_edges_node } from "../../types/ServiceList";
import { Webhook_webhook, Webhook_webhook_events } from "../../types/Webhook";
import WebhookEvents from "../WebhookEvents";
import WebhookInfo from "../WebhookInfo";
import WebhookStatus from "../WebhookStatus";
export interface FormData {
id: string;
events: Webhook_webhook_events[];
events: string[];
isActive: boolean;
name: string;
secretKey: string | null;
@ -30,7 +29,6 @@ export interface FormData {
export interface WebhookCreatePageProps {
disabled: boolean;
errors: UserError[];
webhook: Webhook_webhook;
services: ServiceList_serviceAccounts_edges_node[];
saveButtonBarState: ConfirmButtonTransitionState;
onBack: () => void;
@ -40,7 +38,6 @@ export interface WebhookCreatePageProps {
const WebhookCreatePage: React.StatelessComponent<WebhookCreatePageProps> = ({
disabled,
errors,
webhook,
saveButtonBarState,
services,
onBack,
@ -48,13 +45,13 @@ const WebhookCreatePage: React.StatelessComponent<WebhookCreatePageProps> = ({
}) => {
const intl = useIntl();
const initialForm: FormData = {
events: maybe(() => webhook.events, []),
id: maybe(() => webhook.id, null),
isActive: maybe(() => webhook.isActive, false),
name: maybe(() => webhook.name, null),
secretKey: maybe(() => webhook.secretKey, ""),
serviceAccount: maybe(() => webhook.serviceAccount, ""),
targetUrl: maybe(() => webhook.targetUrl, "")
events: [],
id: null,
isActive: false,
name: null,
secretKey: "",
serviceAccount: "",
targetUrl: ""
};
return (
@ -66,15 +63,10 @@ const WebhookCreatePage: React.StatelessComponent<WebhookCreatePageProps> = ({
{intl.formatMessage(sectionNames.plugins)}
</AppHeader>
<PageHeader
title={intl.formatMessage(
{
defaultMessage: "Create Webhook",
description: "header"
},
{
pluginName: maybe(() => webhook.name, "...")
}
)}
title={intl.formatMessage({
defaultMessage: "Create Webhook",
description: "header"
})}
/>
<Grid>
<div>

View file

@ -0,0 +1,19 @@
import { storiesOf } from "@storybook/react";
import React from "react";
import Decorator from "@saleor/storybook/Decorator";
import WebhookDeleteDialog, {
WebhookDeleteDialogProps
} from "./WebhookDeleteDialog";
const props: WebhookDeleteDialogProps = {
confirmButtonState: "default",
name: "Magento Importer",
onClose: () => undefined,
onConfirm: () => undefined,
open: true
};
storiesOf("Views / Services / Delete service", module)
.addDecorator(Decorator)
.add("default", () => <WebhookDeleteDialog {...props} />);

View file

@ -0,0 +1,50 @@
import DialogContentText from "@material-ui/core/DialogContentText";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import ActionDialog from "@saleor/components/ActionDialog";
import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton";
export interface WebhookDeleteDialogProps {
confirmButtonState: ConfirmButtonTransitionState;
open: boolean;
name: string;
onClose: () => void;
onConfirm: () => void;
}
const WebhookDeleteDialog: React.FC<WebhookDeleteDialogProps> = ({
confirmButtonState,
open,
name,
onClose,
onConfirm
}) => {
const intl = useIntl();
return (
<ActionDialog
confirmButtonState={confirmButtonState}
open={open}
onClose={onClose}
onConfirm={onConfirm}
title={intl.formatMessage({
defaultMessage: "Delete Webhook",
description: "dialog header"
})}
variant="delete"
>
<DialogContentText>
<FormattedMessage
defaultMessage="Are you sure you want to delete {name}?"
description="delete webhook"
values={{
name: <strong>{name}</strong>
}}
/>
</DialogContentText>
</ActionDialog>
);
};
WebhookDeleteDialog.displayName = "WebhookDeleteDialog";
export default WebhookDeleteDialog;

View file

@ -0,0 +1,2 @@
export { default } from "./WebhookDeleteDialog";
export * from "./WebhookDeleteDialog";

View file

@ -28,7 +28,7 @@ const WebhookEvents: React.StatelessComponent<WebhookEventsProps> = ({
onChange
}) => {
const intl = useIntl();
const [events, setEvents] = React.useState();
const [events, setEvents] = React.useState(data.events);
const eventsEnum = Object.values(WebhookEventTypeEnum);
@ -42,10 +42,13 @@ const WebhookEvents: React.StatelessComponent<WebhookEventsProps> = ({
}
};
console.log(data.events);
const eventsOnChange = event => {
const newData = [events];
addOrRemove(newData, event.name);
const newData = events;
addOrRemove(newData, event.target.name);
setEvents(newData);
console.log(events.indexOf(event.target.name));
};
return (
@ -69,7 +72,7 @@ const WebhookEvents: React.StatelessComponent<WebhookEventsProps> = ({
<ControlledCheckbox
name={event}
label={event}
checked={data.events[event]}
checked={events.includes(WebhookEventTypeEnum[event])}
onChange={eventsOnChange}
disabled={disabled}
/>

View file

@ -3,7 +3,6 @@ import CardContent from "@material-ui/core/CardContent";
import Typography from "@material-ui/core/Typography";
import CardTitle from "@saleor/components/CardTitle";
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
import { FormErrors } from "@saleor/types";
import React from "react";
import { useIntl } from "react-intl";

View file

@ -13,14 +13,14 @@ import { WebhookEventTypeEnum } from "@saleor/types/globalTypes";
import React from "react";
import { useIntl } from "react-intl";
import { ServiceList_serviceAccounts_edges_node } from "../../types/ServiceList";
import { Webhook_webhook, Webhook_webhook_events } from "../../types/Webhook";
import { Webhook_webhook } from "../../types/Webhook";
import WebhookEvents from "../WebhookEvents";
import WebhookInfo from "../WebhookInfo";
import WebhookStatus from "../WebhookStatus";
export interface FormData {
id: string;
events: Webhook_webhook_events[];
events: string[];
isActive: boolean;
name: string;
secretKey: string | null;
@ -35,6 +35,7 @@ export interface WebhooksDetailsPageProps {
services: ServiceList_serviceAccounts_edges_node[];
saveButtonBarState: ConfirmButtonTransitionState;
onBack: () => void;
onDelete: () => void;
onSubmit: (data: FormData) => void;
}
@ -47,11 +48,12 @@ const WebhooksDetailsPage: React.StatelessComponent<
saveButtonBarState,
services,
onBack,
onDelete,
onSubmit
}) => {
const intl = useIntl();
const initialForm: FormData = {
events: maybe(() => webhook.events, []),
events: maybe(() => webhook.events, []).map(event => event.eventType),
id: maybe(() => webhook.id, null),
isActive: maybe(() => webhook.isActive, false),
name: maybe(() => webhook.name, ""),
@ -107,6 +109,7 @@ const WebhooksDetailsPage: React.StatelessComponent<
state={saveButtonBarState}
onCancel={onBack}
onSave={submit}
onDelete={onDelete}
/>
</Container>
);

View file

@ -1,4 +1,5 @@
import Card from "@material-ui/core/Card";
import IconButton from "@material-ui/core/IconButton";
import {
createStyles,
Theme,
@ -12,6 +13,7 @@ import TableFooter from "@material-ui/core/TableFooter";
import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";
import EditIcon from "@material-ui/icons/Edit";
import DeleteIcon from "@material-ui/icons/Delete";
import React from "react";
import { useIntl } from "react-intl";
@ -23,6 +25,7 @@ import { Webhooks_webhooks_edges_node } from "../../types/Webhooks";
export interface WebhooksListProps extends ListProps {
webhooks: Webhooks_webhooks_edges_node[];
onRemove: (id: string) => void;
}
const styles = (theme: Theme) =>
@ -56,6 +59,7 @@ const WebhooksList = withStyles(styles, { name: "PluginList" })(
onNextPage,
pageInfo,
onRowClick,
onRemove,
onUpdateListSettings,
onPreviousPage
}: WebhooksListProps & WithStyles<typeof styles>) => {
@ -126,6 +130,14 @@ const WebhooksList = withStyles(styles, { name: "PluginList" })(
>
<EditIcon />
</div>
<IconButton
color="primary"
onClick={
webhook ? () => onRemove(webhook.id) : undefined
}
>
<DeleteIcon />
</IconButton>
</TableCell>
</TableRow>
);

View file

@ -13,6 +13,7 @@ import WebhooksList from "../WebhooksList/WebhooksList";
export interface WebhooksListPageProps extends PageListProps {
webhooks: Webhooks_webhooks_edges_node[];
onBack: () => void;
onRemove: () => void;
}
const WebhooksListPage: React.StatelessComponent<WebhooksListPageProps> = ({
@ -23,6 +24,7 @@ const WebhooksListPage: React.StatelessComponent<WebhooksListPageProps> = ({
onNextPage,
onPreviousPage,
onRowClick,
onRemove,
onUpdateListSettings,
pageInfo,
webhooks
@ -47,6 +49,7 @@ const WebhooksListPage: React.StatelessComponent<WebhooksListPageProps> = ({
webhooks={webhooks}
onNextPage={onNextPage}
onPreviousPage={onPreviousPage}
onRemove={onRemove}
onUpdateListSettings={onUpdateListSettings}
onRowClick={onRowClick}
pageInfo={pageInfo}

View file

@ -57,4 +57,4 @@ const webhookDelete = gql`
export const TypedWebhookDelete = TypedMutation<
WebhookDelete,
WebhookDeleteVariables
>(WebhookDelete);
>(webhookDelete);

View file

@ -1,17 +1,21 @@
import { stringify as stringifyQs } from "qs";
import urlJoin from "url-join";
import { Pagination, SingleAction } from "../types";
import { Dialog, Pagination, SingleAction } from "../types";
export const webhooksSection = "/webhooks/";
export const webhooksListPath = webhooksSection;
export type WebhooksListUrlQueryParams = Pagination & SingleAction;
export type WebhookListUrlDialog = "remove";
export type WebhooksListUrlQueryParams = Dialog<WebhookListUrlDialog> &
Pagination &
SingleAction;
export const webhooksListUrl = (params?: WebhooksListUrlQueryParams) =>
webhooksListPath + "?" + stringifyQs(params);
export const webhooksPath = (id: string) => urlJoin(webhooksSection, id);
export type WebhooksUrlQueryParams = SingleAction;
export type WebhookUrlDialog = "remove";
export type WebhooksUrlQueryParams = Dialog<WebhookUrlDialog> & SingleAction;
export const webhooksUrl = (id: string, params?: WebhooksUrlQueryParams) =>
webhooksPath(encodeURIComponent(id)) + "?" + stringifyQs(params);

View file

@ -1,9 +1,9 @@
import { WindowTitle } from "@saleor/components/WindowTitle";
import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier";
import useShop from "@saleor/hooks/useShop";
import { commonMessages } from "@saleor/intl";
import { WebhookCreate as WebhookCreateData } from "@saleor/webhooks/types/WebhookCreate";
import { WebhookEventTypeEnum } from "@saleor/types/globalTypes";
import React from "react";
import { useIntl } from "react-intl";
@ -28,7 +28,6 @@ export const WebhooksCreate: React.StatelessComponent<
const navigate = useNavigator();
const notify = useNotifier();
const intl = useIntl();
const shop = useShop();
const onSubmit = (data: WebhookCreateData) => {
if (data.webhookCreate.errors.length === 0) {
@ -48,7 +47,7 @@ export const WebhooksCreate: React.StatelessComponent<
WebhookCreate({
variables: {
input: {
events: data.events,
events: [WebhookEventTypeEnum.ALL_EVENTS],
isActive: data.isActive,
name: data.name,
secretKey: data.secretKey,
@ -66,7 +65,7 @@ export const WebhooksCreate: React.StatelessComponent<
return (
<TypedServiceListQuery displayLoader variables={{ first: 99 }}>
{({ data, loading }) => {
{({ data }) => {
return (
<>
<WindowTitle
@ -84,10 +83,8 @@ export const WebhooksCreate: React.StatelessComponent<
services={maybe(() =>
data.serviceAccounts.edges.map(edge => edge.node)
)}
loading={loading}
onBack={handleBack}
onSubmit={handleSubmit}
permissions={maybe(() => shop.permissions)}
saveButtonBarState={formTransitionState}
/>
</>

View file

@ -1,14 +1,22 @@
import { WindowTitle } from "@saleor/components/WindowTitle";
import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier";
import { commonMessages } from "@saleor/intl";
import WebhookDeleteDialog from "@saleor/webhooks/components/WebhookDeleteDialog";
import { WebhookDelete } from "@saleor/webhooks/types/WebhookDelete";
import React from "react";
import { useIntl } from "react-intl";
import { getMutationState, maybe } from "../../misc";
import WebhooksDetailsPage from "../components/WebhooksDetailsPage";
import { TypedWebhookUpdate } from "../mutations";
import { TypedWebhookDelete, TypedWebhookUpdate } from "../mutations";
import { TypedServiceListQuery, TypedWebhooksDetailsQuery } from "../queries";
import { webhooksListUrl, WebhooksListUrlQueryParams } from "../urls";
import {
webhooksListUrl,
WebhooksListUrlQueryParams,
webhooksUrl,
WebhookUrlDialog
} from "../urls";
export interface WebhooksDetailsProps {
id: string;
@ -17,82 +25,138 @@ export interface WebhooksDetailsProps {
export const WebhooksDetails: React.StatelessComponent<
WebhooksDetailsProps
> = ({ id }) => {
> = ({ id, params }) => {
const navigate = useNavigator();
const notify = useNotifier();
const intl = useIntl();
const closeModal = () =>
navigate(
webhooksUrl(id, {
...params,
action: undefined,
id: undefined
}),
true
);
const openModal = (action: WebhookUrlDialog, tokenId?: string) =>
navigate(
webhooksUrl(id, {
...params,
action,
id: tokenId
})
);
const onWebhookDelete = (data: WebhookDelete) => {
if (data.webhookDelete.errors.length === 0) {
notify({
text: intl.formatMessage(commonMessages.savedChanges)
});
navigate(webhooksListUrl());
}
};
return (
<TypedWebhookUpdate>
{(webhookUpdate, webhookUpdateOpts) => (
<TypedWebhooksDetailsQuery variables={{ id }}>
{WebhookDetails => {
const formTransitionState = getMutationState(
webhookUpdateOpts.called,
webhookUpdateOpts.loading,
maybe(() => webhookUpdateOpts.data.webhookUpdate.errors)
);
<TypedWebhookDelete onCompleted={onWebhookDelete}>
{(webhookDelete, webhookDeleteOpts) => (
<TypedWebhooksDetailsQuery variables={{ id }}>
{WebhookDetails => {
const formTransitionState = getMutationState(
webhookUpdateOpts.called,
webhookUpdateOpts.loading,
maybe(() => webhookUpdateOpts.data.webhookUpdate.errors)
);
const formErrors = maybe(
() => webhookUpdateOpts.data.webhookUpdate.errors,
[]
);
const handleRemoveConfirm = () =>
webhookDelete({
variables: {
id
}
});
if (formErrors.length) {
formErrors.map(error => {
notify({
text: error.message
});
});
} else {
if (webhookUpdateOpts.data) {
notify({
text: intl.formatMessage({
defaultMessage: "Succesfully updated plugin settings",
description: "plugin success message"
})
});
}
}
const formErrors = maybe(
() => webhookUpdateOpts.data.webhookUpdate.errors,
[]
);
return (
<TypedServiceListQuery variables={{ first: 99 }}>
{({ data }) => (
<>
<WindowTitle
title={maybe(() => WebhookDetails.data.webhook.name)}
/>
<WebhooksDetailsPage
disabled={WebhookDetails.loading}
errors={formErrors}
saveButtonBarState={formTransitionState}
webhook={maybe(() => WebhookDetails.data.webhook)}
services={maybe(() =>
data.serviceAccounts.edges.map(edge => edge.node)
)}
onBack={() => navigate(webhooksListUrl())}
onSubmit={data => {
webhookUpdate({
variables: {
id,
input: {
events: data.events,
isActive: data.isActive,
name: data.name,
secretKey: data.secretKey,
serviceAccount: data.serviceAccount,
targetUrl: data.targetUrl
}
}
});
}}
/>
</>
)}
</TypedServiceListQuery>
);
}}
</TypedWebhooksDetailsQuery>
if (formErrors.length) {
formErrors.map(error => {
notify({
text: error.message
});
});
} else {
if (webhookUpdateOpts.data) {
notify({
text: intl.formatMessage({
defaultMessage: "Succesfully updated plugin settings",
description: "plugin success message"
})
});
}
}
const deleteTransitionState = getMutationState(
webhookDeleteOpts.called,
webhookDeleteOpts.loading,
maybe(() => webhookDeleteOpts.data.webhookDelete.errors)
);
return (
<TypedServiceListQuery variables={{ first: 99 }}>
{({ data }) => (
<>
<WindowTitle
title={maybe(() => WebhookDetails.data.webhook.name)}
/>
<WebhooksDetailsPage
disabled={WebhookDetails.loading}
errors={formErrors}
saveButtonBarState={formTransitionState}
webhook={maybe(() => WebhookDetails.data.webhook)}
services={maybe(() =>
data.serviceAccounts.edges.map(edge => edge.node)
)}
onBack={() => navigate(webhooksListUrl())}
onDelete={() => openModal("remove")}
onSubmit={data => {
webhookUpdate({
variables: {
id,
input: {
events: data.events,
isActive: data.isActive,
name: data.name,
secretKey: data.secretKey,
serviceAccount: data.serviceAccount,
targetUrl: data.targetUrl
}
}
});
}}
/>
<WebhookDeleteDialog
confirmButtonState={deleteTransitionState}
name={maybe(
() => WebhookDetails.data.webhook.name,
"..."
)}
onClose={closeModal}
onConfirm={handleRemoveConfirm}
open={params.action === "remove"}
/>
</>
)}
</TypedServiceListQuery>
);
}}
</TypedWebhooksDetailsQuery>
)}
</TypedWebhookDelete>
)}
</TypedWebhookUpdate>
);

View file

@ -1,19 +1,27 @@
import { configurationMenuUrl } from "@saleor/configuration";
import useListSettings from "@saleor/hooks/useListSettings";
import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier";
import usePaginator, {
createPaginationState
} from "@saleor/hooks/usePaginator";
import { maybe } from "@saleor/misc";
import { commonMessages } from "@saleor/intl";
import { getMutationState, maybe } from "@saleor/misc";
import { ListViews } from "@saleor/types";
import WebhookDeleteDialog from "@saleor/webhooks/components/WebhookDeleteDialog";
import { WebhookDelete } from "@saleor/webhooks/types/WebhookDelete";
import React from "react";
import { useIntl } from "react-intl";
import WebhooksListPage from "../components/WebhooksListPage/WebhooksListPage";
import { TypedWebhookDelete } from "../mutations";
import { TypedWebhooksListQuery } from "../queries";
import {
webhooksAddUrl,
webhooksListUrl,
WebhooksListUrlQueryParams,
webhooksUrl
webhooksUrl,
WebhookUrlDialog
} from "../urls";
interface WebhooksListProps {
@ -25,36 +33,107 @@ export const WebhooksList: React.StatelessComponent<WebhooksListProps> = ({
}) => {
const navigate = useNavigator();
const paginate = usePaginator();
const notify = useNotifier();
const intl = useIntl();
const { updateListSettings, settings } = useListSettings(
ListViews.WEBHOOK_LIST
);
const paginationState = createPaginationState(settings.rowNumber, params);
const closeModal = () =>
navigate(
webhooksListUrl({
...params,
action: undefined,
id: undefined
}),
true
);
const openModal = (action: WebhookUrlDialog, id?: string) =>
navigate(
webhooksListUrl({
...params,
action,
id
})
);
const onWebhookDelete = (data: WebhookDelete) => {
if (data.webhookDelete.errors.length === 0) {
notify({
text: intl.formatMessage(commonMessages.savedChanges)
});
navigate(webhooksListUrl());
}
};
return (
<TypedWebhooksListQuery displayLoader variables={paginationState}>
{({ data, loading }) => {
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
maybe(() => data.webhooks.pageInfo),
paginationState,
params
);
return (
<>
<WebhooksListPage
disabled={loading}
settings={settings}
webhooks={maybe(() => data.webhooks.edges.map(edge => edge.node))}
pageInfo={pageInfo}
onAdd={() => navigate(webhooksAddUrl)}
onBack={() => navigate(configurationMenuUrl)}
onNextPage={loadNextPage}
onPreviousPage={loadPreviousPage}
onUpdateListSettings={updateListSettings}
onRowClick={id => () => navigate(webhooksUrl(id))}
/>
</>
);
}}
{({ data, loading }) => (
<TypedWebhookDelete onCompleted={onWebhookDelete}>
{(webhookDelete, webhookDeleteOpts) => {
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
maybe(() => data.webhooks.pageInfo),
paginationState,
params
);
const handleRemove = (id: string) =>
navigate(
webhooksListUrl({
...params,
action: "remove",
id
})
);
const handleRemoveConfirm = () =>
webhookDelete({
variables: {
id
}
});
const deleteTransitionState = getMutationState(
webhookDeleteOpts.called,
webhookDeleteOpts.loading,
maybe(() => webhookDeleteOpts.data.webhookDelete.errors)
);
return (
<>
<WebhooksListPage
disabled={loading}
settings={settings}
webhooks={maybe(() =>
data.webhooks.edges.map(edge => edge.node)
)}
pageInfo={pageInfo}
onAdd={() => navigate(webhooksAddUrl)}
onBack={() => navigate(configurationMenuUrl)}
onNextPage={loadNextPage}
onPreviousPage={loadPreviousPage}
onRemove={handleRemove}
onUpdateListSettings={updateListSettings}
onRowClick={id => () => navigate(webhooksUrl(id))}
/>
<WebhookDeleteDialog
confirmButtonState={deleteTransitionState}
name={maybe(
() =>
data.webhooks.edges.find(
edge => edge.node.id === params.id
).node.name,
"..."
)}
onClose={closeModal}
onConfirm={handleRemoveConfirm}
open={params.action === "remove"}
/>
</>
);
}}
</TypedWebhookDelete>
)}
</TypedWebhooksListQuery>
);
};