mirror of
https://github.com/dstotijn/hetty.git
synced 2025-07-01 18:47:29 -04:00
Add project settings for intercept
This commit is contained in:
@ -17,7 +17,12 @@
|
||||
"prettier/prettier": ["error"],
|
||||
"@next/next/no-css-tags": "off",
|
||||
"no-unused-vars": "off",
|
||||
"@typescript-eslint/no-unused-vars": "error",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
"ignoreRestSiblings": true
|
||||
}
|
||||
],
|
||||
|
||||
"import/default": "off",
|
||||
|
||||
|
@ -41,6 +41,7 @@ export enum Page {
|
||||
ProxyLogs,
|
||||
Sender,
|
||||
Scope,
|
||||
Settings,
|
||||
}
|
||||
|
||||
const drawerWidth = 240;
|
||||
|
@ -1,11 +1,13 @@
|
||||
import CancelIcon from "@mui/icons-material/Cancel";
|
||||
import SendIcon from "@mui/icons-material/Send";
|
||||
import { Alert, Box, Button, CircularProgress, Typography } from "@mui/material";
|
||||
import SettingsIcon from "@mui/icons-material/Settings";
|
||||
import { Alert, Box, Button, CircularProgress, IconButton, Tooltip, Typography } from "@mui/material";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import { useInterceptedRequests } from "lib/InterceptedRequestsContext";
|
||||
import { KeyValuePair, sortKeyValuePairs } from "lib/components/KeyValuePair";
|
||||
import Link from "lib/components/Link";
|
||||
import RequestTabs from "lib/components/RequestTabs";
|
||||
import Response from "lib/components/Response";
|
||||
import SplitPane from "lib/components/SplitPane";
|
||||
@ -201,6 +203,11 @@ function EditRequest(): JSX.Element {
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Tooltip title="Intercept settings">
|
||||
<IconButton LinkComponent={Link} href="/settings#intercept">
|
||||
<SettingsIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
{modifyResult.error && (
|
||||
<Alert severity="error" sx={{ mt: 1 }}>
|
||||
|
@ -2,6 +2,7 @@ import CloseIcon from "@mui/icons-material/Close";
|
||||
import DeleteIcon from "@mui/icons-material/Delete";
|
||||
import DescriptionIcon from "@mui/icons-material/Description";
|
||||
import LaunchIcon from "@mui/icons-material/Launch";
|
||||
import SettingsIcon from "@mui/icons-material/Settings";
|
||||
import { Alert } from "@mui/lab";
|
||||
import {
|
||||
Avatar,
|
||||
@ -29,6 +30,7 @@ import React, { useState } from "react";
|
||||
|
||||
import useOpenProjectMutation from "../hooks/useOpenProjectMutation";
|
||||
|
||||
import Link, { NextLinkComposed } from "lib/components/Link";
|
||||
import {
|
||||
ProjectsQuery,
|
||||
useCloseProjectMutation,
|
||||
@ -179,6 +181,11 @@ function ProjectList(): JSX.Element {
|
||||
{project.name} {project.isActive && <em>(Active)</em>}
|
||||
</ListItemText>
|
||||
<ListItemSecondaryAction>
|
||||
<Tooltip title="Project settings">
|
||||
<IconButton LinkComponent={Link} href="/settings" disabled={!project.isActive}>
|
||||
<SettingsIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
{project.isActive && (
|
||||
<Tooltip title="Close project">
|
||||
<IconButton onClick={() => closeProject()}>
|
||||
|
12
admin/src/features/projects/graphql/activeProject.graphql
Normal file
12
admin/src/features/projects/graphql/activeProject.graphql
Normal file
@ -0,0 +1,12 @@
|
||||
query ActiveProject {
|
||||
activeProject {
|
||||
id
|
||||
name
|
||||
isActive
|
||||
settings {
|
||||
intercept {
|
||||
enabled
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,11 +4,13 @@ import { Alert } from "@mui/lab";
|
||||
import { Badge, Button, IconButton, Tooltip } from "@mui/material";
|
||||
import Link from "next/link";
|
||||
|
||||
import { useActiveProject } from "lib/ActiveProjectContext";
|
||||
import { useInterceptedRequests } from "lib/InterceptedRequestsContext";
|
||||
import { ConfirmationDialog, useConfirmationDialog } from "lib/components/ConfirmationDialog";
|
||||
import { HttpRequestLogsDocument, useClearHttpRequestLogMutation } from "lib/graphql/generated";
|
||||
|
||||
function Actions(): JSX.Element {
|
||||
const activeProject = useActiveProject();
|
||||
const interceptedRequests = useInterceptedRequests();
|
||||
const [clearHTTPRequestLog, clearLogsResult] = useClearHttpRequestLogMutation({
|
||||
refetchQueries: [{ query: HttpRequestLogsDocument }],
|
||||
@ -27,23 +29,25 @@ function Actions(): JSX.Element {
|
||||
|
||||
{clearLogsResult.error && <Alert severity="error">Failed to clear HTTP logs: {clearLogsResult.error}</Alert>}
|
||||
|
||||
<Link href="/proxy/intercept/?id=" passHref>
|
||||
<Button
|
||||
variant="contained"
|
||||
disabled={interceptedRequests === null || interceptedRequests.length === 0}
|
||||
color="primary"
|
||||
component="a"
|
||||
size="large"
|
||||
startIcon={
|
||||
<Badge color="error" badgeContent={interceptedRequests?.length || 0}>
|
||||
<AltRouteIcon />
|
||||
</Badge>
|
||||
}
|
||||
sx={{ mr: 1 }}
|
||||
>
|
||||
Review Intercepted…
|
||||
</Button>
|
||||
</Link>
|
||||
{activeProject?.settings.intercept.enabled && (
|
||||
<Link href="/proxy/intercept/?id=" passHref>
|
||||
<Button
|
||||
variant="contained"
|
||||
disabled={interceptedRequests === null || interceptedRequests.length === 0}
|
||||
color="primary"
|
||||
component="a"
|
||||
size="large"
|
||||
startIcon={
|
||||
<Badge color="error" badgeContent={interceptedRequests?.length || 0}>
|
||||
<AltRouteIcon />
|
||||
</Badge>
|
||||
}
|
||||
sx={{ mr: 1 }}
|
||||
>
|
||||
Review Intercepted…
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
|
||||
<Tooltip title="Clear all">
|
||||
<IconButton onClick={clearHTTPConfirmationDialog.open}>
|
||||
|
95
admin/src/features/settings/components/Settings.tsx
Normal file
95
admin/src/features/settings/components/Settings.tsx
Normal file
@ -0,0 +1,95 @@
|
||||
import { useApolloClient } from "@apollo/client";
|
||||
import { TabContext, TabPanel } from "@mui/lab";
|
||||
import TabList from "@mui/lab/TabList";
|
||||
import { Box, FormControl, FormControlLabel, FormHelperText, Switch, Tab, Typography } from "@mui/material";
|
||||
import { SwitchBaseProps } from "@mui/material/internal/SwitchBase";
|
||||
import { useState } from "react";
|
||||
|
||||
import { useActiveProject } from "lib/ActiveProjectContext";
|
||||
import Link from "lib/components/Link";
|
||||
import { ActiveProjectDocument, useUpdateInterceptSettingsMutation } from "lib/graphql/generated";
|
||||
|
||||
enum TabValue {
|
||||
Intercept = "intercept",
|
||||
}
|
||||
|
||||
export default function Settings(): JSX.Element {
|
||||
const client = useApolloClient();
|
||||
const activeProject = useActiveProject();
|
||||
const [updateInterceptSettings, updateIntercepSettingsResult] = useUpdateInterceptSettingsMutation();
|
||||
|
||||
const handleInterceptEnabled: SwitchBaseProps["onChange"] = (_, checked) => {
|
||||
updateInterceptSettings({
|
||||
variables: {
|
||||
input: {
|
||||
enabled: checked,
|
||||
},
|
||||
},
|
||||
onCompleted(data) {
|
||||
client.cache.updateQuery({ query: ActiveProjectDocument }, (cachedData) => ({
|
||||
activeProject: {
|
||||
...cachedData.activeProject,
|
||||
settings: {
|
||||
intercept: data.updateInterceptSettings,
|
||||
},
|
||||
},
|
||||
}));
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const [tabValue, setTabValue] = useState(TabValue.Intercept);
|
||||
|
||||
const tabSx = {
|
||||
textTransform: "none",
|
||||
};
|
||||
|
||||
return (
|
||||
<Box p={4}>
|
||||
<Typography variant="h4" sx={{ mb: 2 }}>
|
||||
Settings
|
||||
</Typography>
|
||||
<Typography paragraph sx={{ mb: 4 }}>
|
||||
Settings allow you to tweak the behaviour of Hetty’s features.
|
||||
</Typography>
|
||||
<Typography variant="h6" sx={{ mb: 2 }}>
|
||||
Project settings
|
||||
</Typography>
|
||||
{!activeProject && (
|
||||
<Typography paragraph>
|
||||
There is no project active. To configure project settings, first <Link href="/projects">open a project</Link>.
|
||||
</Typography>
|
||||
)}
|
||||
{activeProject && (
|
||||
<>
|
||||
<TabContext value={tabValue}>
|
||||
<TabList onChange={(_, value) => setTabValue(value)} sx={{ borderBottom: 1, borderColor: "divider" }}>
|
||||
<Tab value={TabValue.Intercept} label="Intercept" sx={tabSx} />
|
||||
</TabList>
|
||||
|
||||
<TabPanel value={TabValue.Intercept} sx={{ px: 0 }}>
|
||||
<FormControl>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
disabled={updateIntercepSettingsResult.loading}
|
||||
onChange={handleInterceptEnabled}
|
||||
checked={activeProject.settings.intercept.enabled}
|
||||
/>
|
||||
}
|
||||
label="Enable proxy interception"
|
||||
labelPlacement="start"
|
||||
sx={{ display: "inline-block", m: 0 }}
|
||||
/>
|
||||
<FormHelperText>
|
||||
When enabled, incoming HTTP requests to the proxy are stalled for{" "}
|
||||
<Link href="/proxy/intercept">manual review</Link>.
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</TabPanel>
|
||||
</TabContext>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
mutation UpdateInterceptSettings($input: UpdateInterceptSettingsInput!) {
|
||||
updateInterceptSettings(input: $input) {
|
||||
enabled
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import React, { createContext, useContext } from "react";
|
||||
|
||||
import { Project, useProjectsQuery } from "./graphql/generated";
|
||||
import { Project, useActiveProjectQuery } from "./graphql/generated";
|
||||
|
||||
const ActiveProjectContext = createContext<Project | null>(null);
|
||||
|
||||
@ -9,8 +9,8 @@ interface Props {
|
||||
}
|
||||
|
||||
export function ActiveProjectProvider({ children }: Props): JSX.Element {
|
||||
const { data } = useProjectsQuery();
|
||||
const project = data?.projects.find((project) => project.isActive) || null;
|
||||
const { data } = useActiveProjectQuery();
|
||||
const project = data?.activeProject || null;
|
||||
|
||||
return <ActiveProjectContext.Provider value={project}>{children}</ActiveProjectContext.Provider>;
|
||||
}
|
||||
|
94
admin/src/lib/components/Link.tsx
Normal file
94
admin/src/lib/components/Link.tsx
Normal file
@ -0,0 +1,94 @@
|
||||
import MuiLink, { LinkProps as MuiLinkProps } from "@mui/material/Link";
|
||||
import { styled } from "@mui/material/styles";
|
||||
import clsx from "clsx";
|
||||
import NextLink, { LinkProps as NextLinkProps } from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import * as React from "react";
|
||||
|
||||
// Add support for the sx prop for consistency with the other branches.
|
||||
const Anchor = styled("a")({});
|
||||
|
||||
interface NextLinkComposedProps
|
||||
extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, "href">,
|
||||
Omit<NextLinkProps, "href" | "as"> {
|
||||
to: NextLinkProps["href"];
|
||||
linkAs?: NextLinkProps["as"];
|
||||
}
|
||||
|
||||
export const NextLinkComposed = React.forwardRef<HTMLAnchorElement, NextLinkComposedProps>(function NextLinkComposed(
|
||||
props,
|
||||
ref
|
||||
) {
|
||||
const { to, linkAs, replace, scroll, shallow, prefetch, locale, ...other } = props;
|
||||
|
||||
return (
|
||||
<NextLink
|
||||
href={to}
|
||||
prefetch={prefetch}
|
||||
as={linkAs}
|
||||
replace={replace}
|
||||
scroll={scroll}
|
||||
shallow={shallow}
|
||||
passHref
|
||||
locale={locale}
|
||||
>
|
||||
<Anchor ref={ref} {...other} />
|
||||
</NextLink>
|
||||
);
|
||||
});
|
||||
|
||||
export type LinkProps = {
|
||||
activeClassName?: string;
|
||||
as?: NextLinkProps["as"];
|
||||
href: NextLinkProps["href"];
|
||||
linkAs?: NextLinkProps["as"]; // Useful when the as prop is shallow by styled().
|
||||
noLinkStyle?: boolean;
|
||||
} & Omit<NextLinkComposedProps, "to" | "linkAs" | "href"> &
|
||||
Omit<MuiLinkProps, "href">;
|
||||
|
||||
// A styled version of the Next.js Link component:
|
||||
// https://nextjs.org/docs/api-reference/next/link
|
||||
const Link = React.forwardRef<HTMLAnchorElement, LinkProps>(function Link(props, ref) {
|
||||
const {
|
||||
activeClassName = "active",
|
||||
as,
|
||||
className: classNameProps,
|
||||
href,
|
||||
linkAs: linkAsProp,
|
||||
locale,
|
||||
noLinkStyle,
|
||||
prefetch,
|
||||
replace,
|
||||
role, // Link don't have roles.
|
||||
scroll,
|
||||
shallow,
|
||||
...other
|
||||
} = props;
|
||||
|
||||
const router = useRouter();
|
||||
const pathname = typeof href === "string" ? href : href.pathname;
|
||||
const className = clsx(classNameProps, {
|
||||
[activeClassName]: router.pathname === pathname && activeClassName,
|
||||
});
|
||||
|
||||
const isExternal = typeof href === "string" && (href.indexOf("http") === 0 || href.indexOf("mailto:") === 0);
|
||||
|
||||
if (isExternal) {
|
||||
if (noLinkStyle) {
|
||||
return <Anchor className={className} href={href} ref={ref} {...other} />;
|
||||
}
|
||||
|
||||
return <MuiLink className={className} href={href} ref={ref} {...other} />;
|
||||
}
|
||||
|
||||
const linkAs = linkAsProp || as;
|
||||
const nextjsProps = { to: href, linkAs, replace, scroll, shallow, prefetch, locale };
|
||||
|
||||
if (noLinkStyle) {
|
||||
return <NextLinkComposed className={className} ref={ref} {...nextjsProps} {...other} />;
|
||||
}
|
||||
|
||||
return <MuiLink component={NextLinkComposed} className={className} ref={ref} {...nextjsProps} {...other} />;
|
||||
});
|
||||
|
||||
export default Link;
|
@ -116,6 +116,11 @@ export type HttpResponseLog = {
|
||||
statusReason: Scalars['String'];
|
||||
};
|
||||
|
||||
export type InterceptSettings = {
|
||||
__typename?: 'InterceptSettings';
|
||||
enabled: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
export type ModifyRequestInput = {
|
||||
body?: InputMaybe<Scalars['String']>;
|
||||
headers?: InputMaybe<Array<HttpHeaderInput>>;
|
||||
@ -146,6 +151,7 @@ export type Mutation = {
|
||||
setHttpRequestLogFilter?: Maybe<HttpRequestLogFilter>;
|
||||
setScope: Array<ScopeRule>;
|
||||
setSenderRequestFilter?: Maybe<SenderRequestFilter>;
|
||||
updateInterceptSettings: InterceptSettings;
|
||||
};
|
||||
|
||||
|
||||
@ -203,11 +209,22 @@ export type MutationSetSenderRequestFilterArgs = {
|
||||
filter?: InputMaybe<SenderRequestFilterInput>;
|
||||
};
|
||||
|
||||
|
||||
export type MutationUpdateInterceptSettingsArgs = {
|
||||
input: UpdateInterceptSettingsInput;
|
||||
};
|
||||
|
||||
export type Project = {
|
||||
__typename?: 'Project';
|
||||
id: Scalars['ID'];
|
||||
isActive: Scalars['Boolean'];
|
||||
name: Scalars['String'];
|
||||
settings: ProjectSettings;
|
||||
};
|
||||
|
||||
export type ProjectSettings = {
|
||||
__typename?: 'ProjectSettings';
|
||||
intercept: InterceptSettings;
|
||||
};
|
||||
|
||||
export type Query = {
|
||||
@ -296,6 +313,10 @@ export type SenderRequestInput = {
|
||||
url: Scalars['URL'];
|
||||
};
|
||||
|
||||
export type UpdateInterceptSettingsInput = {
|
||||
enabled: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
export type CancelRequestMutationVariables = Exact<{
|
||||
id: Scalars['ID'];
|
||||
}>;
|
||||
@ -317,6 +338,11 @@ export type ModifyRequestMutationVariables = Exact<{
|
||||
|
||||
export type ModifyRequestMutation = { __typename?: 'Mutation', modifyRequest: { __typename?: 'ModifyRequestResult', success: boolean } };
|
||||
|
||||
export type ActiveProjectQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type ActiveProjectQuery = { __typename?: 'Query', activeProject?: { __typename?: 'Project', id: string, name: string, isActive: boolean, settings: { __typename?: 'ProjectSettings', intercept: { __typename?: 'InterceptSettings', enabled: boolean } } } | null };
|
||||
|
||||
export type CloseProjectMutationVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
@ -422,6 +448,13 @@ export type GetSenderRequestsQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
export type GetSenderRequestsQuery = { __typename?: 'Query', senderRequests: Array<{ __typename?: 'SenderRequest', id: string, url: any, method: HttpMethod, response?: { __typename?: 'HttpResponseLog', id: string, statusCode: number, statusReason: string } | null }> };
|
||||
|
||||
export type UpdateInterceptSettingsMutationVariables = Exact<{
|
||||
input: UpdateInterceptSettingsInput;
|
||||
}>;
|
||||
|
||||
|
||||
export type UpdateInterceptSettingsMutation = { __typename?: 'Mutation', updateInterceptSettings: { __typename?: 'InterceptSettings', enabled: boolean } };
|
||||
|
||||
export type GetInterceptedRequestsQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
@ -537,6 +570,47 @@ export function useModifyRequestMutation(baseOptions?: Apollo.MutationHookOption
|
||||
export type ModifyRequestMutationHookResult = ReturnType<typeof useModifyRequestMutation>;
|
||||
export type ModifyRequestMutationResult = Apollo.MutationResult<ModifyRequestMutation>;
|
||||
export type ModifyRequestMutationOptions = Apollo.BaseMutationOptions<ModifyRequestMutation, ModifyRequestMutationVariables>;
|
||||
export const ActiveProjectDocument = gql`
|
||||
query ActiveProject {
|
||||
activeProject {
|
||||
id
|
||||
name
|
||||
isActive
|
||||
settings {
|
||||
intercept {
|
||||
enabled
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useActiveProjectQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useActiveProjectQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useActiveProjectQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useActiveProjectQuery({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useActiveProjectQuery(baseOptions?: Apollo.QueryHookOptions<ActiveProjectQuery, ActiveProjectQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<ActiveProjectQuery, ActiveProjectQueryVariables>(ActiveProjectDocument, options);
|
||||
}
|
||||
export function useActiveProjectLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ActiveProjectQuery, ActiveProjectQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<ActiveProjectQuery, ActiveProjectQueryVariables>(ActiveProjectDocument, options);
|
||||
}
|
||||
export type ActiveProjectQueryHookResult = ReturnType<typeof useActiveProjectQuery>;
|
||||
export type ActiveProjectLazyQueryHookResult = ReturnType<typeof useActiveProjectLazyQuery>;
|
||||
export type ActiveProjectQueryResult = Apollo.QueryResult<ActiveProjectQuery, ActiveProjectQueryVariables>;
|
||||
export const CloseProjectDocument = gql`
|
||||
mutation CloseProject {
|
||||
closeProject {
|
||||
@ -1166,6 +1240,39 @@ export function useGetSenderRequestsLazyQuery(baseOptions?: Apollo.LazyQueryHook
|
||||
export type GetSenderRequestsQueryHookResult = ReturnType<typeof useGetSenderRequestsQuery>;
|
||||
export type GetSenderRequestsLazyQueryHookResult = ReturnType<typeof useGetSenderRequestsLazyQuery>;
|
||||
export type GetSenderRequestsQueryResult = Apollo.QueryResult<GetSenderRequestsQuery, GetSenderRequestsQueryVariables>;
|
||||
export const UpdateInterceptSettingsDocument = gql`
|
||||
mutation UpdateInterceptSettings($input: UpdateInterceptSettingsInput!) {
|
||||
updateInterceptSettings(input: $input) {
|
||||
enabled
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type UpdateInterceptSettingsMutationFn = Apollo.MutationFunction<UpdateInterceptSettingsMutation, UpdateInterceptSettingsMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useUpdateInterceptSettingsMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useUpdateInterceptSettingsMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useUpdateInterceptSettingsMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [updateInterceptSettingsMutation, { data, loading, error }] = useUpdateInterceptSettingsMutation({
|
||||
* variables: {
|
||||
* input: // value for 'input'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useUpdateInterceptSettingsMutation(baseOptions?: Apollo.MutationHookOptions<UpdateInterceptSettingsMutation, UpdateInterceptSettingsMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useMutation<UpdateInterceptSettingsMutation, UpdateInterceptSettingsMutationVariables>(UpdateInterceptSettingsDocument, options);
|
||||
}
|
||||
export type UpdateInterceptSettingsMutationHookResult = ReturnType<typeof useUpdateInterceptSettingsMutation>;
|
||||
export type UpdateInterceptSettingsMutationResult = Apollo.MutationResult<UpdateInterceptSettingsMutation>;
|
||||
export type UpdateInterceptSettingsMutationOptions = Apollo.BaseMutationOptions<UpdateInterceptSettingsMutation, UpdateInterceptSettingsMutationVariables>;
|
||||
export const GetInterceptedRequestsDocument = gql`
|
||||
query GetInterceptedRequests {
|
||||
interceptedRequests {
|
||||
|
12
admin/src/pages/settings/index.tsx
Normal file
12
admin/src/pages/settings/index.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import { Layout, Page } from "features/Layout";
|
||||
import Settings from "features/settings/components/Settings";
|
||||
|
||||
function Index(): JSX.Element {
|
||||
return (
|
||||
<Layout page={Page.Settings} title="Settings">
|
||||
<Settings />
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
export default Index;
|
Reference in New Issue
Block a user