1
0
mirror of https://github.com/checktheroads/hyperglass synced 2024-05-11 05:55:08 +00:00
2020-05-29 17:47:53 -07:00

252 lines
6.2 KiB
JavaScript

import * as React from "react";
import {
Flex,
Icon,
Popover,
PopoverArrow,
PopoverContent,
PopoverTrigger,
Text,
Tooltip,
useColorMode
} from "@chakra-ui/core";
import { MdLastPage } from "react-icons/md";
import dayjs from "dayjs";
import relativeTimePlugin from "dayjs/plugin/relativeTime";
import utcPlugin from "dayjs/plugin/utc";
import useConfig from "~/components/HyperglassProvider";
import Table from "~/components/Table/index";
dayjs.extend(relativeTimePlugin);
dayjs.extend(utcPlugin);
const isActiveColor = {
true: { dark: "green.300", light: "green.500" },
false: { dark: "gray.300", light: "gray.500" }
};
const arrowColor = {
true: { dark: "blackAlpha.500", light: "blackAlpha.500" },
false: { dark: "whiteAlpha.300", light: "blackAlpha.500" }
};
const rpkiIcon = ["not-allowed", "check-circle", "warning", "question"];
const rpkiColor = {
true: {
dark: ["red.500", "green.600", "yellow.500", "gray.800"],
light: ["red.500", "green.500", "yellow.500", "gray.600"]
},
false: {
dark: ["red.300", "green.300", "yellow.300", "gray.300"],
light: ["red.400", "green.500", "yellow.400", "gray.500"]
}
};
const makeColumns = fields => {
return fields.map(pair => {
const [header, accessor, align] = pair;
let columnConfig = {
Header: header,
accessor: accessor,
align: align,
hidden: false
};
if (align === null) {
columnConfig.hidden = true;
}
return columnConfig;
});
};
// const longestASNLength = asPath => {
// if (asPath.length === 0) {
// return 0
// }
// const longest = asPath.reduce((l, c) => {
// const strLongest = String(l);
// const strCurrent = String(c);
// return strCurrent.length > strLongest.length ? strCurrent : strLongest;
// });
// return longest.length;
// };
const MonoField = ({ v, ...props }) => (
<Text fontSize="sm" fontFamily="mono" {...props}>
{v}
</Text>
);
const Active = ({ isActive }) => {
const { colorMode } = useColorMode();
return (
<Icon
name={isActive ? "check-circle" : "warning"}
color={isActiveColor[isActive][colorMode]}
/>
);
};
const Age = ({ inSeconds }) => {
const now = dayjs.utc();
const then = now.subtract(inSeconds, "seconds");
return (
<Tooltip
hasArrow
label={then.toString().replace("GMT", "UTC")}
placement="right"
>
<Text fontSize="sm">{now.to(then, true)}</Text>
</Tooltip>
);
};
const Weight = ({ weight, winningWeight }) => {
const fixMeText =
winningWeight === "low"
? "Lower Weight is Preferred"
: "Higher Weight is Preferred";
return (
<Tooltip hasArrow label={fixMeText} placement="right">
<Text fontSize="sm" fontFamily="mono">
{weight}
</Text>
</Tooltip>
);
};
const ASPath = ({ path, active }) => {
const { colorMode } = useColorMode();
if (path.length === 0) {
return <Icon as={MdLastPage} />;
}
let paths = [];
path.map((asn, i) => {
const asnStr = String(asn);
i !== 0 &&
paths.push(
<Icon
name="chevron-right"
key={`separator-${i}`}
color={arrowColor[active][colorMode]}
/>
);
paths.push(
<Text
fontSize="sm"
as="span"
whiteSpace="pre"
fontFamily="mono"
key={`as-${asnStr}-${i}`}
>
{asnStr}
</Text>
);
});
return paths;
};
const Communities = ({ communities }) => {
const { colorMode } = useColorMode();
let component;
communities.length === 0
? (component = (
<Tooltip placement="right" hasArrow label="No Communities">
<Icon name="question-outline" />
</Tooltip>
))
: (component = (
<Popover trigger="hover" placement="right">
<PopoverTrigger>
<Icon name="view" />
</PopoverTrigger>
<PopoverContent
textAlign="left"
p={4}
maxW="fit-content"
color={colorMode === "dark" ? "white" : "black"}
>
<PopoverArrow />
{communities.map(c => (
<MonoField fontWeight="normal" v={c} key={c.replace(":", "-")} />
))}
</PopoverContent>
</Popover>
));
return component;
};
const RPKIState = ({ state, active }) => {
const { web } = useConfig();
const { colorMode } = useColorMode();
const stateText = [
web.text.rpki_invalid,
web.text.rpki_valid,
web.text.rpki_unknown,
web.text.rpki_unverified
];
return (
<Tooltip
hasArrow
placement="right"
label={stateText[state] ?? stateText[3]}
>
<Icon
name={rpkiIcon[state]}
color={rpkiColor[active][colorMode][state]}
/>
</Tooltip>
);
};
const Cell = ({ data, rawData, longestASN }) => {
const component = {
prefix: <MonoField v={data.value} />,
active: <Active isActive={data.value} />,
age: <Age inSeconds={data.value} />,
weight: (
<Weight weight={data.value} winningWeight={rawData.winning_weight} />
),
med: <MonoField v={data.value} />,
local_preference: <MonoField v={data.value} />,
as_path: (
<ASPath
path={data.value}
active={data.row.values.active}
longestASN={longestASN}
/>
),
communities: <Communities communities={data.value} />,
next_hop: <MonoField v={data.value} />,
source_as: <MonoField v={data.value} />,
source_rid: <MonoField v={data.value} />,
peer_rid: <MonoField v={data.value} />,
rpki_state: <RPKIState state={data.value} active={data.row.values.active} />
};
return component[data.column.id] ?? <> </>;
};
const BGPTable = ({ children: data, ...props }) => {
const config = useConfig();
const columns = makeColumns(config.parsed_data_fields);
// const allASN = data.routes.map(r => r.as_path).flat();
// const asLength = longestASNLength(allASN);
return (
<Flex my={8} maxW={["100%", "100%", "100%", "100%"]} w="100%" {...props}>
<Table
columns={columns}
data={data.routes}
rowHighlightProp="active"
cellRender={d => <Cell data={d} rawData={data} />}
bordersHorizontal
rowHighlightBg="green"
/>
</Flex>
);
};
BGPTable.displayName = "BGPTable";
export default BGPTable;