feat: style app connections
This commit is contained in:
@@ -11,7 +11,8 @@ const connectionType = new GraphQLObjectType({
|
|||||||
key: { type: GraphQLString },
|
key: { type: GraphQLString },
|
||||||
data: { type: connectionDataType },
|
data: { type: connectionDataType },
|
||||||
verified: { type: GraphQLBoolean },
|
verified: { type: GraphQLBoolean },
|
||||||
app: { type: appType }
|
app: { type: appType },
|
||||||
|
createdAt: { type: GraphQLString }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@@ -13,12 +13,14 @@
|
|||||||
"@testing-library/user-event": "^12.1.10",
|
"@testing-library/user-event": "^12.1.10",
|
||||||
"@types/jest": "^26.0.15",
|
"@types/jest": "^26.0.15",
|
||||||
"@types/lodash.template": "^4.5.0",
|
"@types/lodash.template": "^4.5.0",
|
||||||
|
"@types/luxon": "^2.0.8",
|
||||||
"@types/node": "^12.0.0",
|
"@types/node": "^12.0.0",
|
||||||
"@types/react": "^17.0.0",
|
"@types/react": "^17.0.0",
|
||||||
"@types/react-dom": "^17.0.0",
|
"@types/react-dom": "^17.0.0",
|
||||||
"clipboard-copy": "^4.0.1",
|
"clipboard-copy": "^4.0.1",
|
||||||
"graphql": "^15.6.0",
|
"graphql": "^15.6.0",
|
||||||
"lodash.template": "^4.5.0",
|
"lodash.template": "^4.5.0",
|
||||||
|
"luxon": "^2.2.0",
|
||||||
"notistack": "^2.0.2",
|
"notistack": "^2.0.2",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
|
@@ -2,9 +2,11 @@ import * as React from 'react';
|
|||||||
import { useLazyQuery, useMutation } from '@apollo/client';
|
import { useLazyQuery, useMutation } from '@apollo/client';
|
||||||
import Card from '@mui/material/Card';
|
import Card from '@mui/material/Card';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
|
import Stack from '@mui/material/Stack';
|
||||||
import CardActionArea from '@mui/material/CardActionArea';
|
import CardActionArea from '@mui/material/CardActionArea';
|
||||||
import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
|
import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
|
||||||
import { useSnackbar } from 'notistack';
|
import { useSnackbar } from 'notistack';
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
|
|
||||||
import { DELETE_CONNECTION } from 'graphql/mutations/delete-connection';
|
import { DELETE_CONNECTION } from 'graphql/mutations/delete-connection';
|
||||||
import { TEST_CONNECTION } from 'graphql/queries/test-connection';
|
import { TEST_CONNECTION } from 'graphql/queries/test-connection';
|
||||||
@@ -17,7 +19,14 @@ type AppConnectionRowProps = {
|
|||||||
connection: Connection;
|
connection: Connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
const countTranslation = (value: React.ReactNode) => (<><strong>{value}</strong><br /></>);
|
const countTranslation = (value: React.ReactNode) => (
|
||||||
|
<>
|
||||||
|
<Typography variant="body1">
|
||||||
|
{value}
|
||||||
|
</Typography>
|
||||||
|
<br />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
function AppConnectionRow(props: AppConnectionRowProps) {
|
function AppConnectionRow(props: AppConnectionRowProps) {
|
||||||
const { enqueueSnackbar } = useSnackbar();
|
const { enqueueSnackbar } = useSnackbar();
|
||||||
@@ -25,7 +34,7 @@ function AppConnectionRow(props: AppConnectionRowProps) {
|
|||||||
const [deleteConnection] = useMutation(DELETE_CONNECTION);
|
const [deleteConnection] = useMutation(DELETE_CONNECTION);
|
||||||
|
|
||||||
const formatMessage = useFormatMessage();
|
const formatMessage = useFormatMessage();
|
||||||
const { id, key, data, verified } = props.connection;
|
const { id, key, data, verified, createdAt } = props.connection;
|
||||||
|
|
||||||
const contextButtonRef = React.useRef<SVGSVGElement | null>(null);
|
const contextButtonRef = React.useRef<SVGSVGElement | null>(null);
|
||||||
const [anchorEl, setAnchorEl] = React.useState<SVGSVGElement | null>(null);
|
const [anchorEl, setAnchorEl] = React.useState<SVGSVGElement | null>(null);
|
||||||
@@ -57,23 +66,34 @@ function AppConnectionRow(props: AppConnectionRowProps) {
|
|||||||
}
|
}
|
||||||
}, [deleteConnection, id, testConnection, formatMessage, enqueueSnackbar]);
|
}, [deleteConnection, id, testConnection, formatMessage, enqueueSnackbar]);
|
||||||
|
|
||||||
|
const relativeCreatedAt = DateTime.fromMillis(parseInt(createdAt, 10)).toRelative();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Card sx={{ my: 2 }}>
|
<Card sx={{ my: 2 }}>
|
||||||
<CardActionArea onClick={onContextMenuClick}>
|
<CardActionArea onClick={onContextMenuClick}>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Box>
|
<Stack
|
||||||
|
direction="column"
|
||||||
|
justifyContent="center"
|
||||||
|
alignItems="flex-start"
|
||||||
|
spacing={1}
|
||||||
|
>
|
||||||
<Typography variant="h6">
|
<Typography variant="h6">
|
||||||
{data.screenName}
|
{data.screenName}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
|
||||||
|
<Typography variant="caption">
|
||||||
|
{formatMessage('connection.addedAt', { datetime: relativeCreatedAt })}
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
{testCalled && !testLoading && (verified ? 'yes' : 'no')}
|
{testCalled && !testLoading && (verified ? 'yes' : 'no')}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box sx={{ px: 2 }}>
|
<Box sx={{ px: 2 }}>
|
||||||
<Typography variant="body2">
|
<Typography variant="caption" color="textSecondary" sx={{ display: ['none', 'inline-block'] }}>
|
||||||
{formatMessage('connection.flowCount', { count: countTranslation(0) })}
|
{formatMessage('connection.flowCount', { count: countTranslation(0) })}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
@@ -1,18 +1,34 @@
|
|||||||
|
import * as React from 'react';
|
||||||
import { useQuery } from '@apollo/client';
|
import { useQuery } from '@apollo/client';
|
||||||
import { GET_APP_CONNECTIONS } from 'graphql/queries/get-app-connections';
|
|
||||||
|
|
||||||
|
import { GET_APP_CONNECTIONS } from 'graphql/queries/get-app-connections';
|
||||||
import AppConnectionRow from 'components/AppConnectionRow';
|
import AppConnectionRow from 'components/AppConnectionRow';
|
||||||
|
import NoResultFound from 'components/NoResultFound';
|
||||||
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
import type { Connection } from 'types/connection';
|
import type { Connection } from 'types/connection';
|
||||||
|
import * as URLS from 'config/urls';
|
||||||
|
|
||||||
type AppConnectionsProps = {
|
type AppConnectionsProps = {
|
||||||
appKey: String;
|
appKey: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AppConnections(props: AppConnectionsProps) {
|
export default function AppConnections(props: AppConnectionsProps) {
|
||||||
const { appKey } = props;
|
const { appKey } = props;
|
||||||
|
const formatMessage = useFormatMessage();
|
||||||
const { data } = useQuery(GET_APP_CONNECTIONS, { variables: { key: appKey } });
|
const { data } = useQuery(GET_APP_CONNECTIONS, { variables: { key: appKey } });
|
||||||
const appConnections: Connection[] = data?.getApp?.connections || [];
|
const appConnections: Connection[] = data?.getApp?.connections || [];
|
||||||
|
|
||||||
|
const hasConnections = appConnections?.length;
|
||||||
|
|
||||||
|
if (!hasConnections) {
|
||||||
|
return (
|
||||||
|
<NoResultFound
|
||||||
|
to={URLS.APP_ADD_CONNECTION(appKey)}
|
||||||
|
text={formatMessage('app.noConnections')}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{appConnections.map((appConnection: Connection) => (
|
{appConnections.map((appConnection: Connection) => (
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import Avatar from '@mui/material/Avatar';
|
import Avatar from '@mui/material/Avatar';
|
||||||
|
import type { AvatarProps } from '@mui/material/Avatar';
|
||||||
|
|
||||||
type AppIconProps = {
|
type AppIconProps = {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -11,18 +12,19 @@ const inlineImgStyle: React.CSSProperties = {
|
|||||||
objectFit: 'contain',
|
objectFit: 'contain',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function AppIcon(props: AppIconProps) {
|
export default function AppIcon(props: AppIconProps & AvatarProps) {
|
||||||
const { name, url } = props;
|
const { name, url, sx = {}, ...restProps } = props;
|
||||||
const color = url ? 'white' : props.color
|
const color = url ? 'white' : props.color
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Avatar
|
<Avatar
|
||||||
component="span"
|
component="span"
|
||||||
variant="square"
|
variant="square"
|
||||||
sx={{ bgcolor: `#${color}` }}
|
sx={{ bgcolor: `#${color}`, display: 'inline-flex', width: 50, height: 50, ...sx }}
|
||||||
imgProps={{ style: inlineImgStyle }}
|
imgProps={{ style: inlineImgStyle }}
|
||||||
src={url}
|
src={url}
|
||||||
alt={name}
|
alt={name}
|
||||||
|
{...restProps}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@@ -22,6 +22,12 @@ export default function Drawer(props: SwipeableDrawerProps) {
|
|||||||
const matchSmallScreens = useMediaQuery(theme.breakpoints.down('md'), { noSsr: true });
|
const matchSmallScreens = useMediaQuery(theme.breakpoints.down('md'), { noSsr: true });
|
||||||
const formatMessage = useFormatMessage();
|
const formatMessage = useFormatMessage();
|
||||||
|
|
||||||
|
const closeOnClick = (event: React.SyntheticEvent) => {
|
||||||
|
if (matchSmallScreens) {
|
||||||
|
props.onClose(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseDrawer
|
<BaseDrawer
|
||||||
{...props}
|
{...props}
|
||||||
@@ -38,18 +44,21 @@ export default function Drawer(props: SwipeableDrawerProps) {
|
|||||||
icon={<SwapCallsIcon htmlColor={theme.palette.primary.main} />}
|
icon={<SwapCallsIcon htmlColor={theme.palette.primary.main} />}
|
||||||
primary={formatMessage('drawer.flows')}
|
primary={formatMessage('drawer.flows')}
|
||||||
to={URLS.FLOWS}
|
to={URLS.FLOWS}
|
||||||
|
onClick={closeOnClick}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ListItemLink
|
<ListItemLink
|
||||||
icon={<AppsIcon htmlColor={theme.palette.primary.main} />}
|
icon={<AppsIcon htmlColor={theme.palette.primary.main} />}
|
||||||
primary={formatMessage('drawer.apps')}
|
primary={formatMessage('drawer.apps')}
|
||||||
to={URLS.APPS}
|
to={URLS.APPS}
|
||||||
|
onClick={closeOnClick}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ListItemLink
|
<ListItemLink
|
||||||
icon={<ExploreIcon htmlColor={theme.palette.primary.main} />}
|
icon={<ExploreIcon htmlColor={theme.palette.primary.main} />}
|
||||||
primary={formatMessage('drawer.explore')}
|
primary={formatMessage('drawer.explore')}
|
||||||
to={URLS.EXPLORE}
|
to={URLS.EXPLORE}
|
||||||
|
onClick={closeOnClick}
|
||||||
/>
|
/>
|
||||||
</List>
|
</List>
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { useMemo, forwardRef } from 'react';
|
import * as React from 'react';
|
||||||
import { useMatch } from 'react-router-dom';
|
import { useMatch } from 'react-router-dom';
|
||||||
import ListItem from '@mui/material/ListItemButton';
|
import ListItem from '@mui/material/ListItemButton';
|
||||||
import ListItemIcon from '@mui/material/ListItemIcon';
|
import ListItemIcon from '@mui/material/ListItemIcon';
|
||||||
@@ -9,15 +9,16 @@ type ListItemLinkProps = {
|
|||||||
icon: React.ReactNode;
|
icon: React.ReactNode;
|
||||||
primary: string;
|
primary: string;
|
||||||
to: string;
|
to: string;
|
||||||
|
onClick?: (event: React.SyntheticEvent) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ListItemLink(props: ListItemLinkProps) {
|
export default function ListItemLink(props: ListItemLinkProps) {
|
||||||
const { icon, primary, to } = props;
|
const { icon, primary, to, onClick } = props;
|
||||||
const selected = useMatch({ path: to, end: false });
|
const selected = useMatch({ path: to, end: false });
|
||||||
|
|
||||||
const CustomLink = useMemo(
|
const CustomLink = React.useMemo(
|
||||||
() =>
|
() =>
|
||||||
forwardRef<HTMLAnchorElement, Omit<LinkProps, 'to'>>(function InLineLink(
|
React.forwardRef<HTMLAnchorElement, Omit<LinkProps, 'to'>>(function InLineLink(
|
||||||
linkProps,
|
linkProps,
|
||||||
ref,
|
ref,
|
||||||
) {
|
) {
|
||||||
@@ -28,7 +29,12 @@ export default function ListItemLink(props: ListItemLinkProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<li>
|
<li>
|
||||||
<ListItem component={CustomLink} sx={{ pl: { xs: 2, sm: 3 } }} selected={!!selected}>
|
<ListItem
|
||||||
|
component={CustomLink}
|
||||||
|
sx={{ pl: { xs: 2, sm: 3 } }}
|
||||||
|
selected={!!selected}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
<ListItemIcon sx={{ minWidth: 52 }}>{icon}</ListItemIcon>
|
<ListItemIcon sx={{ minWidth: 52 }}>{icon}</ListItemIcon>
|
||||||
<ListItemText primary={primary} primaryTypographyProps={{ variant: 'body1', }} />
|
<ListItemText primary={primary} primaryTypographyProps={{ variant: 'body1', }} />
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
42
packages/web/src/components/NoResultFound/index.tsx
Normal file
42
packages/web/src/components/NoResultFound/index.tsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import type { LinkProps } from 'react-router-dom';
|
||||||
|
import Card from '@mui/material/Card';
|
||||||
|
import AddCircleIcon from '@mui/icons-material/AddCircle';
|
||||||
|
import CardActionArea from '@mui/material/CardActionArea';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import { CardContent } from './style';
|
||||||
|
|
||||||
|
type NoResultFoundProps = {
|
||||||
|
text?: string;
|
||||||
|
to: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function NoResultFound(props: NoResultFoundProps) {
|
||||||
|
const { text, to } = props;
|
||||||
|
|
||||||
|
const ActionAreaLink = React.useMemo(
|
||||||
|
() =>
|
||||||
|
React.forwardRef<HTMLAnchorElement, Omit<LinkProps, 'to'>>(function InlineLink(
|
||||||
|
linkProps,
|
||||||
|
ref,
|
||||||
|
) {
|
||||||
|
return <Link ref={ref} to={to} {...linkProps} />;
|
||||||
|
}),
|
||||||
|
[to],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card elevation={0}>
|
||||||
|
<CardActionArea component={ActionAreaLink} {...props}>
|
||||||
|
<CardContent>
|
||||||
|
<AddCircleIcon color="primary" />
|
||||||
|
|
||||||
|
<Typography variant="body1">
|
||||||
|
{text}
|
||||||
|
</Typography>
|
||||||
|
</CardContent>
|
||||||
|
</CardActionArea>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
};
|
11
packages/web/src/components/NoResultFound/style.ts
Normal file
11
packages/web/src/components/NoResultFound/style.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { styled } from '@mui/material/styles';
|
||||||
|
import MuiCardContent from '@mui/material/CardContent';
|
||||||
|
|
||||||
|
export const CardContent = styled(MuiCardContent)`
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: ${({ theme }) => theme.spacing(2)};
|
||||||
|
min-height: 200px;
|
||||||
|
`;
|
@@ -11,6 +11,7 @@ export const GET_APP_CONNECTIONS = gql`
|
|||||||
data {
|
data {
|
||||||
screenName
|
screenName
|
||||||
}
|
}
|
||||||
|
createdAt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,9 +9,12 @@
|
|||||||
"app.connectionCount": "{count} connections",
|
"app.connectionCount": "{count} connections",
|
||||||
"app.flowCount": "{count} flows",
|
"app.flowCount": "{count} flows",
|
||||||
"app.addConnection": "Add connection",
|
"app.addConnection": "Add connection",
|
||||||
|
"app.createFlow": "Create flow",
|
||||||
"app.settings": "Settings",
|
"app.settings": "Settings",
|
||||||
"app.connections": "Connections",
|
"app.connections": "Connections",
|
||||||
|
"app.noConnections": "You don't have any connections yet.",
|
||||||
"app.flows": "Flows",
|
"app.flows": "Flows",
|
||||||
|
"app.noFlows": "You don't have any flows yet.",
|
||||||
"apps.title": "My Apps",
|
"apps.title": "My Apps",
|
||||||
"apps.addConnection": "Add connection",
|
"apps.addConnection": "Add connection",
|
||||||
"apps.addNewAppConnection": "Add a new app connection",
|
"apps.addNewAppConnection": "Add a new app connection",
|
||||||
@@ -21,5 +24,7 @@
|
|||||||
"connection.testConnection": "Test connection",
|
"connection.testConnection": "Test connection",
|
||||||
"connection.reconnect": "Reconnect",
|
"connection.reconnect": "Reconnect",
|
||||||
"connection.delete": "Delete",
|
"connection.delete": "Delete",
|
||||||
"connection.deletedMessage": "The connection has been deleted."
|
"connection.deletedMessage": "The connection has been deleted.",
|
||||||
|
"connection.addedAt": "Added {datetime}"
|
||||||
|
|
||||||
}
|
}
|
@@ -1,17 +1,20 @@
|
|||||||
|
import * as React from 'react';
|
||||||
import { useQuery } from '@apollo/client';
|
import { useQuery } from '@apollo/client';
|
||||||
import { Link, Route, Navigate, Routes, useParams, useMatch, useNavigate } from 'react-router-dom';
|
import { Link, Route, Navigate, Routes, useParams, useMatch, useNavigate } from 'react-router-dom';
|
||||||
|
import type { LinkProps } from 'react-router-dom';
|
||||||
|
import { useTheme } from '@mui/material/styles';
|
||||||
|
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import Grid from '@mui/material/Grid';
|
import Grid from '@mui/material/Grid';
|
||||||
import Button from '@mui/material/Button';
|
|
||||||
import IconButton from '@mui/material/IconButton';
|
|
||||||
import Tabs from '@mui/material/Tabs';
|
import Tabs from '@mui/material/Tabs';
|
||||||
import Tab from '@mui/material/Tab';
|
import Tab from '@mui/material/Tab';
|
||||||
import SettingsIcon from '@mui/icons-material/Settings';
|
import AddIcon from '@mui/icons-material/Add';
|
||||||
|
|
||||||
import useFormatMessage from 'hooks/useFormatMessage';
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
import { GET_APP } from 'graphql/queries/get-app';
|
import { GET_APP } from 'graphql/queries/get-app';
|
||||||
import * as URLS from 'config/urls';
|
import * as URLS from 'config/urls';
|
||||||
|
|
||||||
|
import ConditionalIconButton from 'components/ConditionalIconButton';
|
||||||
import AppConnections from 'components/AppConnections';
|
import AppConnections from 'components/AppConnections';
|
||||||
import AppFlows from 'components/AppFlows';
|
import AppFlows from 'components/AppFlows';
|
||||||
import AddAppConnection from 'components/AddAppConnection';
|
import AddAppConnection from 'components/AddAppConnection';
|
||||||
@@ -37,8 +40,9 @@ const ReconnectConnection = (props: any) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default function Application() {
|
export default function Application() {
|
||||||
|
const theme = useTheme();
|
||||||
|
const matchSmallScreens = useMediaQuery(theme.breakpoints.down('md'), { noSsr: true });
|
||||||
const formatMessage = useFormatMessage();
|
const formatMessage = useFormatMessage();
|
||||||
const connectionsPathMatch = useMatch({ path: URLS.APP_CONNECTIONS_PATTERN, end: false });
|
const connectionsPathMatch = useMatch({ path: URLS.APP_CONNECTIONS_PATTERN, end: false });
|
||||||
const flowsPathMatch = useMatch({ path: URLS.APP_FLOWS_PATTERN, end: false });
|
const flowsPathMatch = useMatch({ path: URLS.APP_FLOWS_PATTERN, end: false });
|
||||||
@@ -49,34 +53,91 @@ export default function Application() {
|
|||||||
const goToApplicationPage = () => navigate('connections');
|
const goToApplicationPage = () => navigate('connections');
|
||||||
const app = data?.getApp || {};
|
const app = data?.getApp || {};
|
||||||
|
|
||||||
|
const NewConnectionLink = React.useMemo(
|
||||||
|
() =>
|
||||||
|
React.forwardRef<HTMLAnchorElement, Omit<LinkProps, 'to'>>(function InlineLink(
|
||||||
|
linkProps,
|
||||||
|
ref,
|
||||||
|
) {
|
||||||
|
return <Link ref={ref} to={URLS.APP_ADD_CONNECTION(appKey)} {...linkProps} />;
|
||||||
|
}),
|
||||||
|
[appKey],
|
||||||
|
);
|
||||||
|
|
||||||
|
const NewFlowLink = React.useMemo(
|
||||||
|
() =>
|
||||||
|
React.forwardRef<HTMLAnchorElement, Omit<LinkProps, 'to'>>(function InlineLink(
|
||||||
|
linkProps,
|
||||||
|
ref,
|
||||||
|
) {
|
||||||
|
return <Link ref={ref} to={URLS.APP_ADD_CONNECTION(appKey)} {...linkProps} />;
|
||||||
|
}),
|
||||||
|
[appKey],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box sx={{ py: 3 }}>
|
<Box sx={{ py: 3 }}>
|
||||||
<Container>
|
<Container>
|
||||||
<Grid container sx={{ mb: 3 }}>
|
<Grid container sx={{ mb: 3 }} alignItems="center">
|
||||||
<Grid item xs="auto" sx={{ mr: 1.5 }}>
|
<Grid item xs="auto" sx={{ mr: 3 }}>
|
||||||
<AppIcon url={app.iconUrl} color={app.primaryColor} name={app.name} />
|
<AppIcon
|
||||||
|
url={app.iconUrl}
|
||||||
|
color={app.primaryColor}
|
||||||
|
name={app.name}
|
||||||
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid item xs>
|
<Grid item xs>
|
||||||
<PageTitle>{app.name}</PageTitle>
|
<PageTitle>{app.name}</PageTitle>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid item xs="auto" justifyContent="flex-end">
|
<Grid item xs="auto">
|
||||||
<IconButton sx={{ mr: 2 }} title={formatMessage('app.settings')}>
|
<Routes>
|
||||||
<SettingsIcon />
|
<Route
|
||||||
</IconButton>
|
path={`${URLS.FLOWS}/*`}
|
||||||
|
element={
|
||||||
|
<ConditionalIconButton
|
||||||
|
type="submit"
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
size="large"
|
||||||
|
component={NewFlowLink}
|
||||||
|
fullWidth
|
||||||
|
icon={<AddIcon />}
|
||||||
|
>
|
||||||
|
{formatMessage('app.createFlow')}
|
||||||
|
</ConditionalIconButton>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<Button variant="contained" component={Link} to={URLS.APP_ADD_CONNECTION(appKey)}>
|
<Route
|
||||||
|
path={`${URLS.CONNECTIONS}/*`}
|
||||||
|
element={
|
||||||
|
<ConditionalIconButton
|
||||||
|
type="submit"
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
size="large"
|
||||||
|
component={NewConnectionLink}
|
||||||
|
fullWidth
|
||||||
|
icon={<AddIcon />}
|
||||||
|
>
|
||||||
{formatMessage('app.addConnection')}
|
{formatMessage('app.addConnection')}
|
||||||
</Button>
|
</ConditionalIconButton>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Routes>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid item xs>
|
<Grid item xs>
|
||||||
<Box sx={{ borderBottom: 1, borderColor: 'divider', mb: 2 }}>
|
<Box sx={{ borderBottom: 1, borderColor: 'divider', mb: 2 }}>
|
||||||
<Tabs value={connectionsPathMatch?.pattern?.path || flowsPathMatch?.pattern?.path}>
|
<Tabs
|
||||||
|
variant={matchSmallScreens ? 'fullWidth' : undefined}
|
||||||
|
value={connectionsPathMatch?.pattern?.path || flowsPathMatch?.pattern?.path}
|
||||||
|
>
|
||||||
<Tab
|
<Tab
|
||||||
label={formatMessage('app.connections')}
|
label={formatMessage('app.connections')}
|
||||||
to={URLS.APP_CONNECTIONS(appKey)}
|
to={URLS.APP_CONNECTIONS(appKey)}
|
||||||
|
@@ -6,7 +6,6 @@ import Box from '@mui/material/Box';
|
|||||||
import Grid from '@mui/material/Grid';
|
import Grid from '@mui/material/Grid';
|
||||||
import AddIcon from '@mui/icons-material/Add';
|
import AddIcon from '@mui/icons-material/Add';
|
||||||
|
|
||||||
|
|
||||||
import ConditionalIconButton from 'components/ConditionalIconButton';
|
import ConditionalIconButton from 'components/ConditionalIconButton';
|
||||||
import Container from 'components/Container';
|
import Container from 'components/Container';
|
||||||
import AddNewAppConnection from 'components/AddNewAppConnection';
|
import AddNewAppConnection from 'components/AddNewAppConnection';
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import Container from '@mui/material/Container';
|
import Container from 'components/Container';
|
||||||
|
|
||||||
export default function Explore() {
|
export default function Explore() {
|
||||||
return (
|
return (
|
||||||
|
@@ -180,6 +180,9 @@ const extendedTheme = createTheme({
|
|||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
root: {
|
root: {
|
||||||
background: 'rgba(0, 8, 20, 0.64)'
|
background: 'rgba(0, 8, 20, 0.64)'
|
||||||
|
},
|
||||||
|
invisible: {
|
||||||
|
background: 'transparent',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -243,6 +246,15 @@ const extendedTheme = createTheme({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
MuiTab: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
[referenceTheme.breakpoints.up('sm')]: {
|
||||||
|
padding: referenceTheme.spacing(1.5, 3),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
MuiToolbar: {
|
MuiToolbar: {
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
root: {
|
root: {
|
||||||
|
@@ -7,6 +7,7 @@ type Connection = {
|
|||||||
key: string;
|
key: string;
|
||||||
data: ConnectionData;
|
data: ConnectionData;
|
||||||
verified: boolean;
|
verified: boolean;
|
||||||
|
createdAt: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type { Connection, ConnectionData };
|
export type { Connection, ConnectionData };
|
||||||
|
10
yarn.lock
10
yarn.lock
@@ -3206,6 +3206,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.175.tgz#b78dfa959192b01fae0ad90e166478769b215f45"
|
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.175.tgz#b78dfa959192b01fae0ad90e166478769b215f45"
|
||||||
integrity sha512-XmdEOrKQ8a1Y/yxQFOMbC47G/V2VDO1GvMRnl4O75M4GW/abC5tnfzadQYkqEveqRM1dEJGFFegfPNA2vvx2iw==
|
integrity sha512-XmdEOrKQ8a1Y/yxQFOMbC47G/V2VDO1GvMRnl4O75M4GW/abC5tnfzadQYkqEveqRM1dEJGFFegfPNA2vvx2iw==
|
||||||
|
|
||||||
|
"@types/luxon@^2.0.8":
|
||||||
|
version "2.0.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-2.0.8.tgz#a0fdd7ab0b67e08bf1d301232a7fef79b74ded69"
|
||||||
|
integrity sha512-lGmxL6hMEVqXr8w9bL52RUWXVu90o7vH8WQSutQssr2e+w0TNttXx2Zfw2V2lHHHWfW6OGqB8bXDvtKocv19qQ==
|
||||||
|
|
||||||
"@types/mime@^1":
|
"@types/mime@^1":
|
||||||
version "1.3.2"
|
version "1.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a"
|
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a"
|
||||||
@@ -10531,6 +10536,11 @@ lru-cache@^6.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
yallist "^4.0.0"
|
yallist "^4.0.0"
|
||||||
|
|
||||||
|
luxon@^2.2.0:
|
||||||
|
version "2.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/luxon/-/luxon-2.2.0.tgz#f5c4a234ba4016f792488b11aaed2d5bc14c888e"
|
||||||
|
integrity sha512-LwmknessH4jVIseCsizUgveIHwlLv/RQZWC2uDSMfGJs7w8faPUi2JFxfyfMcTPrpNbChTem3Uz6IKRtn+LcIA==
|
||||||
|
|
||||||
lz-string@^1.4.4:
|
lz-string@^1.4.4:
|
||||||
version "1.4.4"
|
version "1.4.4"
|
||||||
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26"
|
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26"
|
||||||
|
Reference in New Issue
Block a user