Add webhook components

This commit is contained in:
Krzysztof Bialoglowicz 2019-10-09 08:01:52 +02:00
parent 2637ec3fdf
commit 4021b8ec62
20 changed files with 869 additions and 0 deletions

View file

@ -202,6 +202,10 @@ export const sectionNames = defineMessages({
vouchers: { vouchers: {
defaultMessage: "Vouchers", defaultMessage: "Vouchers",
description: "vouchers section name" description: "vouchers section name"
},
webhooks: {
defaultMessage: "Webhooks",
description: "webhooks section name"
} }
}); });

View file

@ -0,0 +1,57 @@
import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import TextField from "@material-ui/core/TextField";
import makeStyles from "@material-ui/styles/makeStyles";
import CardTitle from "@saleor/components/CardTitle";
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
import { FormErrors } from "@saleor/types";
import { ConfigurationTypeFieldEnum } from "@saleor/types/globalTypes";
import React from "react";
import { useIntl } from "react-intl";
import { FormData } from "../WebhookDetailsPage";
interface WebhookEventsProps {
data: FormData;
errors: FormErrors<"name" | "configuration">;
disabled: boolean;
onChange: (event: React.ChangeEvent<any>) => void;
fields: Array<{
name: string;
type: ConfigurationTypeFieldEnum | null;
value: string;
helpText: string | null;
label: string | null;
}>;
}
const useStyles = makeStyles(() => ({
item: {
paddingBottom: 10,
paddingTop: 10
}
}));
const WebhookEvents: React.StatelessComponent<WebhookEventsProps> = ({
data,
disabled,
errors,
onChange,
fields
}) => {
const classes = useStyles({});
const intl = useIntl();
return (
<Card>
<CardTitle
title={intl.formatMessage({
defaultMessage: "Events",
description: "section header"
})}
/>
<CardContent></CardContent>
</Card>
);
};
WebhookEvents.displayName = "WebhookEvents";
export default WebhookEvents;

View file

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

View file

@ -0,0 +1,53 @@
import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import Typography from "@material-ui/core/Typography";
import makeStyles from "@material-ui/styles/makeStyles";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import CardTitle from "@saleor/components/CardTitle";
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
import FormSpacer from "@saleor/components/FormSpacer";
import Hr from "@saleor/components/Hr";
import { commonMessages } from "@saleor/intl";
import { FormData } from "../WebhooksDetailsPage";
interface WebhookInfoProps {
data: FormData;
description: string;
name: string;
onChange: (event: React.ChangeEvent<any>) => void;
}
const useStyles = makeStyles(() => ({
status: {
paddingTop: 20
},
title: {
fontSize: 14,
paddingTop: 10
}
}));
const WebhookInfo: React.StatelessComponent<WebhookInfoProps> = ({
data,
description,
name,
onChange
}) => {
const classes = useStyles({});
const intl = useIntl();
return (
<Card>
<CardTitle
title={intl.formatMessage({
defaultMessage: "Plugin Information and Status",
description: "section header"
})}
/>
<CardContent></CardContent>
</Card>
);
};
WebhookInfo.displayName = "WebhookInfo";
export default WebhookInfo;

View file

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

View file

@ -0,0 +1,57 @@
import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import TextField from "@material-ui/core/TextField";
import makeStyles from "@material-ui/styles/makeStyles";
import CardTitle from "@saleor/components/CardTitle";
import ControlledCheckbox from "@saleor/components/ControlledCheckbox";
import { FormErrors } from "@saleor/types";
import { ConfigurationTypeFieldEnum } from "@saleor/types/globalTypes";
import React from "react";
import { useIntl } from "react-intl";
import { FormData } from "../WebhooksDetailsPage";
interface WebhookStatusProps {
data: FormData;
errors: FormErrors<"name" | "configuration">;
disabled: boolean;
onChange: (event: React.ChangeEvent<any>) => void;
fields: Array<{
name: string;
type: ConfigurationTypeFieldEnum | null;
value: string;
helpText: string | null;
label: string | null;
}>;
}
const useStyles = makeStyles(() => ({
item: {
paddingBottom: 10,
paddingTop: 10
}
}));
const WebhookStatus: React.StatelessComponent<WebhookStatusProps> = ({
data,
disabled,
errors,
onChange,
fields
}) => {
const classes = useStyles({});
const intl = useIntl();
return (
<Card>
<CardTitle
title={intl.formatMessage({
defaultMessage: "Webhook Status",
description: "section header"
})}
/>
<CardContent></CardContent>
</Card>
);
};
WebhookStatus.displayName = "WebhookStatus";
export default WebhookStatus;

View file

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

View file

@ -0,0 +1,78 @@
import Typography from "@material-ui/core/Typography";
import AppHeader from "@saleor/components/AppHeader";
import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton";
import Container from "@saleor/components/Container";
import Form from "@saleor/components/Form";
import Grid from "@saleor/components/Grid";
import PageHeader from "@saleor/components/PageHeader";
import SaveButtonBar from "@saleor/components/SaveButtonBar";
import { sectionNames } from "@saleor/intl";
import { maybe } from "@saleor/misc";
import { UserError } from "@saleor/types";
import { ConfigurationItemInput } from "@saleor/types/globalTypes";
import React from "react";
import { useIntl } from "react-intl";
import { Plugin_plugin } from "../../types/Plugin";
import WebhookInfo from "../WebhookInfo";
import WebhookEvents from "../WebhookEvents";
export interface FormData {
active: boolean;
configuration: ConfigurationItemInput[];
}
export interface WebhooksDetailsPageProps {
disabled: boolean;
errors: UserError[];
plugin: Plugin_plugin;
saveButtonBarState: ConfirmButtonTransitionState;
onBack: () => void;
onSubmit: (data: FormData) => void;
}
const WebhooksDetailsPage: React.StatelessComponent<
WebhooksDetailsPageProps
> = ({ disabled, errors, plugin, saveButtonBarState, onBack, onSubmit }) => {
const intl = useIntl();
const initialForm: FormData = {
active: maybe(() => plugin.active, false),
configuration: maybe(() => plugin.configuration, [])
};
return (
<Form errors={errors} initial={initialForm} onSubmit={onSubmit}>
{({ data, errors, hasChanged, submit, set, triggerChange }) => {
return (
<Container>
<AppHeader onBack={onBack}>
{intl.formatMessage(sectionNames.plugins)}
</AppHeader>
<PageHeader
title={intl.formatMessage(
{
defaultMessage: "{pluginName} Details",
description: "header"
},
{
pluginName: maybe(() => plugin.name, "...")
}
)}
/>
<Grid variant="inverted">
<div></div>
</Grid>
<SaveButtonBar
disabled={disabled || !hasChanged}
state={saveButtonBarState}
onCancel={onBack}
onSave={submit}
/>
</Container>
);
}}
</Form>
);
};
WebhooksDetailsPage.displayName = "WebhooksDetailsPage";
export default WebhooksDetailsPage;

View file

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

View file

@ -0,0 +1,155 @@
import Card from "@material-ui/core/Card";
import {
createStyles,
Theme,
withStyles,
WithStyles
} from "@material-ui/core/styles";
import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
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 React from "react";
import { useIntl } from "react-intl";
import Skeleton from "@saleor/components/Skeleton";
import StatusLabel from "@saleor/components/StatusLabel";
import TablePagination from "@saleor/components/TablePagination";
import { translateBoolean } from "@saleor/intl";
import { maybe, renderCollection } from "@saleor/misc";
import { ListProps } from "@saleor/types";
import { Plugins_plugins_edges_node } from "../../types/Plugins";
export interface WebhooksListProps extends ListProps {
webhooks: Plugins_plugins_edges_node[];
}
const styles = (theme: Theme) =>
createStyles({
[theme.breakpoints.up("lg")]: {
colAction: {
"& svg": {
color: theme.palette.primary.main
},
textAlign: "right"
},
colActive: {},
colName: {}
},
colAction: {},
colActive: {},
colName: {},
link: {
cursor: "pointer"
}
});
const numberOfColumns = 4;
const WebhooksList = withStyles(styles, { name: "PluginList" })(
({
classes,
settings,
webhooks,
disabled,
onNextPage,
pageInfo,
onRowClick,
onUpdateListSettings,
onPreviousPage
}: WebhooksListProps & WithStyles<typeof styles>) => {
const intl = useIntl();
return (
<Card>
<Table>
<TableHead>
<TableCell className={classes.colName} padding="dense">
{intl.formatMessage({
defaultMessage: "Name",
description: "webhook name"
})}
</TableCell>
<TableCell className={classes.colActive} padding="dense">
{intl.formatMessage({
defaultMessage: "Service Account",
description: "webhook service account"
})}
</TableCell>
<TableCell className={classes.colAction} padding="dense">
{intl.formatMessage({
defaultMessage: "Action",
description: "user action bar"
})}
</TableCell>
</TableHead>
<TableFooter>
<TableRow>
<TablePagination
colSpan={numberOfColumns}
settings={settings}
hasNextPage={
pageInfo && !disabled ? pageInfo.hasNextPage : false
}
onNextPage={onNextPage}
onUpdateListSettings={onUpdateListSettings}
hasPreviousPage={
pageInfo && !disabled ? pageInfo.hasPreviousPage : false
}
onPreviousPage={onPreviousPage}
/>
</TableRow>
</TableFooter>
<TableBody>
{renderCollection(
webhooks,
webhook => {
return (
// <TableRow
// hover={!!plugin}
// className={!!plugin ? classes.link : undefined}
// onClick={plugin ? onRowClick(plugin.id) : undefined}
// key={plugin ? plugin.id : "skeleton"}
// >
// <TableCell className={classes.colName}>
// {maybe<React.ReactNode>(() => plugin.name, <Skeleton />)}
// </TableCell>
// <TableCell className={classes.colActive}>
// {maybe<React.ReactNode>(
// () => (
// <StatusLabel
// label={translateBoolean(plugin.active, intl)}
// status={plugin.active ? "success" : "error"}
// />
// ),
// <Skeleton />
// )}
// </TableCell>
// <TableCell className={classes.colAction}>
// <div onClick={plugin ? onRowClick(plugin.id) : undefined}>
// <EditIcon />
// </div>
// </TableCell>
// </TableRow>
);
},
() => (
<TableRow>
<TableCell colSpan={numberOfColumns}>
{intl.formatMessage({
defaultMessage: "No plugins found"
})}
</TableCell>
</TableRow>
)
)}
</TableBody>
</Table>
</Card>
);
}
);
WebhooksList.displayName = "WebhooksList";
export default WebhooksList;

View file

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

View file

@ -0,0 +1,49 @@
import React from "react";
import { useIntl } from "react-intl";
import AppHeader from "@saleor/components/AppHeader";
import Container from "@saleor/components/Container";
import PageHeader from "@saleor/components/PageHeader";
import { sectionNames } from "@saleor/intl";
import { PageListProps } from "@saleor/types";
import { Plugins_plugins_edges_node } from "../../types/Plugins";
import WebhooksList from "../WebhooksList/WebhooksList";
export interface WebhooksListPageProps extends PageListProps {
plugins: Plugins_plugins_edges_node[];
onBack: () => void;
}
const WebhooksListPage: React.StatelessComponent<WebhooksListPageProps> = ({
disabled,
settings,
onBack,
onNextPage,
onPreviousPage,
onRowClick,
onUpdateListSettings,
pageInfo,
webhooks
}) => {
const intl = useIntl();
return (
<Container>
<AppHeader onBack={onBack}>
{intl.formatMessage(sectionNames.configuration)}
</AppHeader>
<PageHeader title={intl.formatMessage(sectionNames.webhooks)} />
<WebhooksList
disabled={disabled}
settings={settings}
webhooks={webhooks}
onNextPage={onNextPage}
onPreviousPage={onPreviousPage}
onUpdateListSettings={onUpdateListSettings}
onRowClick={onRowClick}
pageInfo={pageInfo}
/>
</Container>
);
};
WebhooksListPage.displayName = "WebhooksListPage";
export default WebhooksListPage;

View file

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

57
src/webhooks/fixtures.ts Normal file
View file

@ -0,0 +1,57 @@
import { ConfigurationTypeFieldEnum } from "@saleor/types/globalTypes";
import { Plugin_plugin } from "./types/Plugin";
import { Plugins_plugins_edges_node } from "./types/Plugins";
export const pluginList: Plugins_plugins_edges_node[] = [
{
__typename: "Plugin",
active: true,
description:
"Lorem ipsum dolor sit amet enim. Etiam ullamcorper. Suspendisse a pellentesque dui, non felis. Maecenas malesuada elit lectus felis, malesuada ultricies. Curabitur et ligula. Ut molestie a, ultricies porta urna. Vestibulum commodo volutpat a, convallis ac, laoreet enim. Phasellus fermentum in, dolor. Pellentesque facilisis. Nulla imperdiet sit amet magna.",
id: "Jzx123sEt==",
name: "Avalara"
},
{
__typename: "Plugin",
active: false,
description:
"Lorem ipsum dolor sit amet enim. Etiam ullamcorper. Suspendisse a pellentesque dui, non felis. Maecenas malesuada elit lectus felis, malesuada ultricies. Curabitur et ligula. Ut molestie a, ultricies porta urna. Vestibulum commodo volutpat a, convallis ac, laoreet enim. Phasellus fermentum in, dolor. Pellentesque facilisis. Nulla imperdiet sit amet magna.",
id: "Jzx123sEt==",
name: "VatLayer"
}
];
export const plugin: Plugin_plugin = {
__typename: "Plugin",
active: true,
configuration: [
{
__typename: "ConfigurationItem",
helpText: "Provide user or account details",
label: "Username or account",
name: "Username or account",
type: ConfigurationTypeFieldEnum.STRING,
value: ""
},
{
__typename: "ConfigurationItem",
helpText: "Provide password or license details",
label: "Password or license",
name: "Password or license",
type: ConfigurationTypeFieldEnum.STRING,
value: ""
},
{
__typename: "ConfigurationItem",
helpText: "Determines if Saleor should use Avatax sandbox API.",
label: "Use sandbox",
name: "Use sandbox",
type: ConfigurationTypeFieldEnum.BOOLEAN,
value: "true"
}
],
description:
"Lorem ipsum dolor sit amet enim. Etiam ullamcorper. Suspendisse a pellentesque dui, non felis. Maecenas malesuada elit lectus felis, malesuada ultricies. Curabitur et ligula. Ut molestie a, ultricies porta urna. Vestibulum commodo volutpat a, convallis ac, laoreet enim. Phasellus fermentum in, dolor. Pellentesque facilisis. Nulla imperdiet sit amet magna.",
id: "UGx1Z2luQ29uZmlndXJhdGlvbjoy",
name: "Username or account"
};

51
src/webhooks/index.tsx Normal file
View file

@ -0,0 +1,51 @@
import { parse as parseQs } from "qs";
import React from "react";
import { useIntl } from "react-intl";
import { Route, RouteComponentProps, Switch } from "react-router-dom";
import { sectionNames } from "@saleor/intl";
import { WindowTitle } from "../components/WindowTitle";
import {
webhooksListPath,
WebhooksListUrlQueryParams,
webhooksPath
} from "./urls";
import PluginsDetailsComponent from "./views/WebhooksDetails";
import PluginsListComponent from "./views/WebhooksList";
const PluginList: React.StatelessComponent<RouteComponentProps<any>> = ({
location
}) => {
const qs = parseQs(location.search.substr(1));
const params: WebhooksListUrlQueryParams = qs;
return <PluginsListComponent params={params} />;
};
const PageDetails: React.StatelessComponent<RouteComponentProps<any>> = ({
match
}) => {
const qs = parseQs(location.search.substr(1));
const params: WebhooksListUrlQueryParams = qs;
return (
<PluginsDetailsComponent
id={decodeURIComponent(match.params.id)}
params={params}
/>
);
};
const Component = () => {
const intl = useIntl();
return (
<>
<WindowTitle title={intl.formatMessage(sectionNames.plugins)} />
<Switch>
<Route exact path={webhooksListPath} component={PluginList} />
<Route path={webhooksPath(":id")} component={PageDetails} />
</Switch>
</>
);
};
export default Component;

60
src/webhooks/mutations.ts Normal file
View file

@ -0,0 +1,60 @@
import gql from "graphql-tag";
import { TypedMutation } from "../mutations";
import { webhooksDetailsFragment } from "./queries";
import { WebhookCreate, WebhookCreateVariables } from "./types/WebhookCreate";
import { WebhookDelete, WebhookDeleteVariables } from "./types/WebhookDelete";
import { WebhookUpdate, WebhookUpdateVariables } from "./types/WebhookUpdate";
const webhookCreate = gql`
${webhooksDetailsFragment}
mutation WebhookCreate($input: WebhookCreateInput!) {
WebhookCreate(input: $input) {
errors {
field
message
}
webhook {
...WebhooksDetailsFragment
}
}
}
`;
export const TypedWebhookCreate = TypedMutation<
WebhookCreate,
WebhookCreateVariables
>(webhookCreate);
const webhookUpdate = gql`
${webhooksDetailsFragment}
mutation WebhookUpdate($id: ID!, $input: WebhookUpdateInput!) {
WebhookUpdate(id: $id, input: $input) {
errors {
field
message
}
webhook {
...WebhooksDetailsFragment
}
}
}
`;
export const TypedWebhookUpdate = TypedMutation<
WebhookUpdate,
WebhookUpdateVariables
>(webhookUpdate);
const WebhookDelete = gql`
mutation WebhookDelete($id: ID!) {
WebhookDelete(id: $id) {
errors {
field
message
}
}
}
`;
export const TypedWebhookDelete = TypedMutation<
WebhookDelete,
WebhookDeleteVariables
>(WebhookDelete);

62
src/webhooks/queries.ts Normal file
View file

@ -0,0 +1,62 @@
import gql from "graphql-tag";
import { TypedQuery } from "../queries";
import { Webhook, WebhookVariables } from "./types/Webhook";
import { Webhooks, WebhooksVariables } from "./types/Webhooks";
export const webhooksFragment = gql`
fragment WebhookFragment on Webhook {
id
events {
eventType
}
isActive
secretKey
targetUrl
serviceAccount {
id
name
}
}
`;
export const webhooksDetailsFragment = gql`
${webhooksFragment}
fragment WebhooksDetailsFragment on Webhook {
...WebhooksFragment
}
`;
const webhooksList = gql`
${webhooksFragment}
query Webhooks($first: Int, $after: String, $last: Int, $before: String) {
webhooks(before: $before, after: $after, first: $first, last: $last) {
edges {
node {
...WebhooksFragment
}
}
pageInfo {
hasPreviousPage
hasNextPage
startCursor
endCursor
}
}
}
`;
export const TypedWebhooksListQuery = TypedQuery<Webhooks, WebhooksVariables>(
webhooksList
);
const webhooksDetails = gql`
${webhooksFragment}
query Webhook($id: ID!) {
webhook(id: $id) {
...WebhooksFragment
}
}
`;
export const TypedWebhooksDetailsQuery = TypedQuery<Webhook, WebhookVariables>(
webhooksDetails
);

16
src/webhooks/urls.ts Normal file
View file

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

View file

@ -0,0 +1,100 @@
import { WindowTitle } from "@saleor/components/WindowTitle";
import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier";
import React from "react";
import { useIntl } from "react-intl";
import { getMutationState, maybe } from "../../misc";
import WebhooksDetailsPage from "../components/WebhooksDetailsPage";
import { TypedPluginUpdate } from "../mutations";
import { TypedPluginsDetailsQuery } from "../queries";
import { pluginsListUrl, PluginsListUrlQueryParams } from "../urls";
export interface PluginsDetailsProps {
id: string;
params: PluginsListUrlQueryParams;
}
export const PluginsDetails: React.StatelessComponent<PluginsDetailsProps> = ({
id
}) => {
const navigate = useNavigator();
const notify = useNotifier();
const intl = useIntl();
return (
<TypedPluginUpdate>
{(pluginUpdate, pluginUpdateOpts) => (
<TypedPluginsDetailsQuery variables={{ id }}>
{PluginDetails => {
const formTransitionState = getMutationState(
pluginUpdateOpts.called,
pluginUpdateOpts.loading,
maybe(() => pluginUpdateOpts.data.pluginUpdate.errors)
);
const formErrors = maybe(
() => pluginUpdateOpts.data.pluginUpdate.errors,
[]
);
if (formErrors.length) {
formErrors.map(error => {
notify({
text: error.message
});
});
} else {
if (pluginUpdateOpts.data) {
notify({
text: intl.formatMessage({
defaultMessage: "Succesfully updated plugin settings",
description: "plugin success message"
})
});
}
}
return (
<>
<WindowTitle
title={maybe(() => PluginDetails.data.plugin.name)}
/>
<WebhooksDetailsPage
disabled={PluginDetails.loading}
errors={formErrors}
saveButtonBarState={formTransitionState}
plugin={maybe(() => PluginDetails.data.plugin)}
onBack={() => navigate(pluginsListUrl())}
onSubmit={formData => {
const configurationInput =
formData.configuration &&
formData.configuration.map(item => {
return {
name: item.name,
value: item.value.toString()
};
});
pluginUpdate({
variables: {
id,
input: {
active: formData.active,
configuration: configurationInput
? configurationInput
: null
}
}
});
}}
/>
</>
);
}}
</TypedPluginsDetailsQuery>
)}
</TypedPluginUpdate>
);
};
PluginsDetails.displayName = "PluginsDetails";
export default PluginsDetails;

View file

@ -0,0 +1,58 @@
import { configurationMenuUrl } from "@saleor/configuration";
import useListSettings from "@saleor/hooks/useListSettings";
import useNavigator from "@saleor/hooks/useNavigator";
import usePaginator, {
createPaginationState
} from "@saleor/hooks/usePaginator";
import { maybe } from "@saleor/misc";
import { ListViews } from "@saleor/types";
import React from "react";
import WebhooksListPage from "../components/WebhooksListPage/PluginsListPage";
import { TypedWebhooksListQuery } from "../queries";
import { WebhooksListUrlQueryParams, webhooksUrl } from "../urls";
interface PluginsListProps {
params: WebhooksListUrlQueryParams;
}
export const PluginsList: React.StatelessComponent<PluginsListProps> = ({
params
}) => {
const navigate = useNavigator();
const paginate = usePaginator();
const { updateListSettings, settings } = useListSettings(
ListViews.PLUGINS_LIST
);
const paginationState = createPaginationState(settings.rowNumber, params);
return (
<TypedWebhooksListQuery displayLoader variables={paginationState}>
{({ data, loading }) => {
const { loadNextPage, loadPreviousPage, pageInfo } = paginate(
maybe(() => data.plugins.pageInfo),
paginationState,
params
);
return (
<>
<WebhooksListPage
disabled={loading}
settings={settings}
webhooks={maybe(() => data.plugins.edges.map(edge => edge.node))}
pageInfo={pageInfo}
onAdd={() => navigate(configurationMenuUrl)}
onBack={() => navigate(configurationMenuUrl)}
onNextPage={loadNextPage}
onPreviousPage={loadPreviousPage}
onUpdateListSettings={updateListSettings}
onRowClick={id => () => navigate(webhooksUrl(id))}
/>
</>
);
}}
</TypedWebhooksListQuery>
);
};
export default PluginsList;