pomerium/ui/src/components/RoutesPage.tsx
Caleb Doxsey 3e90f1e244
proxy: well known service icons (#5453)
* proxy: add logo discovery

* use a static url for testing

* well known service icons

* better fitting avatars
2025-01-30 08:52:59 -07:00

155 lines
3.8 KiB
TypeScript

import {
Avatar,
Box,
Card,
CardActionArea,
CardContent,
CardHeader,
Grid,
IconButton,
Paper,
Snackbar,
Stack,
Typography,
} from "@mui/material";
import React, { FC, useState } from "react";
import { Clipboard, Link } from "react-feather";
import { Route, RoutesPageData } from "../types";
import Section from "./Section";
import SidebarPage from "./SidebarPage";
type RouteCardProps = {
route: Route;
};
const RouteCard: FC<RouteCardProps> = ({ route }) => {
const [showSnackbar, setShowSnackbar] = useState(false);
const handleClick = (evt: React.MouseEvent) => {
if (route.connect_command) {
evt.preventDefault();
navigator.clipboard.writeText(route.connect_command);
setShowSnackbar(true);
}
};
return (
<Card raised={true}>
<CardActionArea href={route.from} target="_blank" onClick={handleClick}>
<CardHeader
avatar={
route.logo_url ? (
<Avatar
src={route.logo_url}
variant="square"
slotProps={{
img: {
sx: {
objectFit: "scale-down",
},
},
}}
/>
) : route.type === "tcp" ? (
<Avatar>TCP</Avatar>
) : route.type === "udp" ? (
<Avatar>UDP</Avatar>
) : (
<Avatar>
<Link />
</Avatar>
)
}
action={
route.connect_command && (
<IconButton title="Copy Command">
<Clipboard />
</IconButton>
)
}
title={route.name}
/>
<CardContent>
{route.description && (
<Typography variant="body2">{route.description}</Typography>
)}
{route.connect_command && (
<Box
component="span"
sx={{ fontFamily: '"DM Mono"', fontSize: "12px" }}
>
{route.connect_command}
</Box>
)}
</CardContent>
</CardActionArea>
<Snackbar
anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
open={showSnackbar}
autoHideDuration={3000}
onClose={() => setShowSnackbar(false)}
message="Copied to Clipboard"
/>
</Card>
);
};
type RoutesSectionProps = {
type: "http" | "tcp" | "udp";
title: string;
allRoutes: Route[];
};
const RoutesSection: FC<RoutesSectionProps> = ({ type, title, allRoutes }) => {
const routes = allRoutes?.filter((r) => r.type === type);
if (routes?.length === 0) {
return <></>;
}
return (
<Section title={title}>
<Grid container spacing={2} justifyContent="center">
{routes?.map((r) => (
<Grid key={r.id} item sx={{ width: 300 }}>
<RouteCard route={r} />
</Grid>
))}
</Grid>
</Section>
);
};
type RoutesPageProps = {
data: RoutesPageData;
};
const RoutesPage: FC<RoutesPageProps> = ({ data }) => {
return (
<SidebarPage>
<Stack spacing={2}>
{data?.routes ? (
<>
<RoutesSection
type={"http"}
title={"HTTP Routes"}
allRoutes={data.routes}
/>
<RoutesSection
type={"tcp"}
title={"TCP Routes"}
allRoutes={data.routes}
/>
<RoutesSection
type={"udp"}
title={"UDP Routes"}
allRoutes={data.routes}
/>
</>
) : (
<Paper sx={{ padding: 3 }}>
<Typography>No accessible routes found</Typography>
</Paper>
)}
</Stack>
</SidebarPage>
);
};
export default RoutesPage;