mirror of
https://github.com/netbirdio/dashboard.git
synced 2026-09-23 00:30:43 -07:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e553f3c83 |
No files matched your search
+1
-1
@@ -53,7 +53,7 @@
|
||||
"framer-motion": "^10.16.4",
|
||||
"ip-cidr": "^3.1.0",
|
||||
"lodash": "^4.17.21",
|
||||
"lucide-react": "^0.383.0",
|
||||
"lucide-react": "^0.287.0",
|
||||
"next": "13.5.5",
|
||||
"next-themes": "^0.2.1",
|
||||
"punycode": "^2.3.1",
|
||||
|
||||
@@ -13,7 +13,6 @@ export interface InputProps
|
||||
icon?: React.ReactNode;
|
||||
error?: string;
|
||||
errorTooltip?: boolean;
|
||||
errorTooltipPosition?: "top" | "top-right";
|
||||
}
|
||||
|
||||
const inputVariants = cva("", {
|
||||
@@ -50,7 +49,6 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
||||
maxWidthClass = "",
|
||||
error,
|
||||
errorTooltip = false,
|
||||
errorTooltipPosition = "top",
|
||||
...props
|
||||
},
|
||||
ref,
|
||||
@@ -107,12 +105,9 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
||||
</div>
|
||||
{error && errorTooltip && (
|
||||
<div
|
||||
className={cn(
|
||||
errorTooltipPosition == "top" &&
|
||||
"absolute right-0 top-2 h-[0px] w-full flex items-center pr-3 justify-center",
|
||||
errorTooltipPosition == "top-right" &&
|
||||
"absolute -right-6 top-2 h-[0px] w-full flex items-center pr-3 justify-end",
|
||||
)}
|
||||
className={
|
||||
"absolute right-0 top-2 h-[0px] w-full flex items-center pr-3 justify-center"
|
||||
}
|
||||
>
|
||||
<FullTooltip
|
||||
content={
|
||||
@@ -125,7 +120,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
||||
</div>
|
||||
}
|
||||
interactive={false}
|
||||
align={errorTooltipPosition == "top" ? "center" : "end"}
|
||||
align={"center"}
|
||||
side={"top"}
|
||||
keepOpen={true}
|
||||
>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { CommandItem } from "@components/Command";
|
||||
import FullTooltip from "@components/FullTooltip";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@components/Popover";
|
||||
import { IconArrowBack } from "@tabler/icons-react";
|
||||
import useFetchApi from "@utils/api";
|
||||
@@ -63,13 +62,8 @@ export function NetworkRouteSelector({
|
||||
const isSearching = search.length > 0;
|
||||
const found =
|
||||
dropdownOptions.filter((item) => {
|
||||
const hasDomains = item?.domains ? item.domains.length > 0 : false;
|
||||
const domains =
|
||||
hasDomains && item?.domains ? item?.domains.join(" ") : "";
|
||||
return (
|
||||
item.network_id.includes(search) ||
|
||||
item.network?.includes(search) ||
|
||||
domains.includes(search)
|
||||
item.network_id.includes(search) || item.network.includes(search)
|
||||
);
|
||||
}).length > 0;
|
||||
return isSearching && !found;
|
||||
@@ -123,7 +117,6 @@ export function NetworkRouteSelector({
|
||||
>
|
||||
{value.network}
|
||||
</div>
|
||||
<DomainList domains={value?.domains} />
|
||||
</div>
|
||||
) : (
|
||||
<span>Select an existing network...</span>
|
||||
@@ -215,11 +208,7 @@ export function NetworkRouteSelector({
|
||||
return (
|
||||
<CommandItem
|
||||
key={option.network + option.network_id}
|
||||
value={
|
||||
option.network +
|
||||
option.network_id +
|
||||
option?.domains?.join(", ")
|
||||
}
|
||||
value={option.network + option.network_id}
|
||||
onSelect={() => {
|
||||
togglePeer(option);
|
||||
setOpen(false);
|
||||
@@ -237,7 +226,6 @@ export function NetworkRouteSelector({
|
||||
>
|
||||
{option.network}
|
||||
</div>
|
||||
<DomainList domains={option?.domains} />
|
||||
</CommandItem>
|
||||
);
|
||||
})}
|
||||
@@ -250,19 +238,3 @@ export function NetworkRouteSelector({
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
function DomainList({ domains }: { domains?: string[] }) {
|
||||
const firstDomain = domains ? domains[0] : "";
|
||||
return (
|
||||
domains &&
|
||||
domains.length > 0 && (
|
||||
<FullTooltip
|
||||
content={<div className={"text-xs max-w-sm"}>{domains.join(", ")}</div>}
|
||||
>
|
||||
<div className={"text-xs text-nb-gray-300"}>
|
||||
{firstDomain} {domains.length > 1 && "+" + (domains.length - 1)}
|
||||
</div>
|
||||
</FullTooltip>
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -10,7 +10,6 @@ interface Props extends IconVariant {
|
||||
className?: string;
|
||||
margin?: string;
|
||||
truncate?: boolean;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
export default function ModalHeader({
|
||||
icon,
|
||||
@@ -20,7 +19,6 @@ export default function ModalHeader({
|
||||
className = "pb-6 px-8",
|
||||
margin = "mt-0",
|
||||
truncate = false,
|
||||
children,
|
||||
}: Props) {
|
||||
return (
|
||||
<div className={cn(className, "min-w-0")}>
|
||||
@@ -28,15 +26,11 @@ export default function ModalHeader({
|
||||
{icon && <SquareIcon color={color} icon={icon} />}
|
||||
<div className={"min-w-0"}>
|
||||
<h2 className={"text-lg my-0 leading-[1.5]"}>{title}</h2>
|
||||
{children ? (
|
||||
<>{children}</>
|
||||
) : (
|
||||
<Paragraph
|
||||
className={cn("text-sm", margin, truncate && "!block truncate")}
|
||||
>
|
||||
{description}
|
||||
</Paragraph>
|
||||
)}
|
||||
<Paragraph
|
||||
className={cn("text-sm", margin, truncate && "!block truncate")}
|
||||
>
|
||||
{description}
|
||||
</Paragraph>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
import Badge from "@components/Badge";
|
||||
import FullTooltip from "@components/FullTooltip";
|
||||
import { GlobeIcon } from "lucide-react";
|
||||
import * as React from "react";
|
||||
|
||||
type Props = {
|
||||
domains: string[];
|
||||
};
|
||||
export const DomainListBadge = ({ domains }: Props) => {
|
||||
const firstDomain = domains.length > 0 ? domains[0] : undefined;
|
||||
|
||||
return (
|
||||
<DomainsTooltip domains={domains}>
|
||||
<div className={"inline-flex items-center gap-2"}>
|
||||
{firstDomain && (
|
||||
<Badge variant={"gray"}>
|
||||
<GlobeIcon size={10} />
|
||||
{firstDomain}
|
||||
</Badge>
|
||||
)}
|
||||
{domains && domains.length > 1 && (
|
||||
<Badge variant={"gray"}>+ {domains.length - 1}</Badge>
|
||||
)}
|
||||
</div>
|
||||
</DomainsTooltip>
|
||||
);
|
||||
};
|
||||
|
||||
export const DomainsTooltip = ({
|
||||
domains,
|
||||
children,
|
||||
className,
|
||||
}: {
|
||||
domains: string[];
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}) => {
|
||||
return (
|
||||
<FullTooltip
|
||||
interactive={false}
|
||||
className={className}
|
||||
content={
|
||||
<div className={"flex flex-col gap-2 items-start"}>
|
||||
{domains.map((domain) => {
|
||||
return (
|
||||
domain && (
|
||||
<div
|
||||
key={domain}
|
||||
className={"flex gap-2 items-center justify-between w-full"}
|
||||
>
|
||||
<div
|
||||
className={
|
||||
"flex gap-2 items-center text-nb-gray-300 text-xs"
|
||||
}
|
||||
>
|
||||
<GlobeIcon size={11} />
|
||||
{domain}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
}
|
||||
disabled={domains.length <= 1}
|
||||
>
|
||||
{children}
|
||||
</FullTooltip>
|
||||
);
|
||||
};
|
||||
@@ -1,88 +0,0 @@
|
||||
import Button from "@components/Button";
|
||||
import { Input } from "@components/Input";
|
||||
import { validator } from "@utils/helpers";
|
||||
import { uniqueId } from "lodash";
|
||||
import { GlobeIcon, MinusCircleIcon } from "lucide-react";
|
||||
import * as React from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { Domain } from "@/interfaces/Domain";
|
||||
|
||||
type Props = {
|
||||
value: Domain;
|
||||
onChange: (d: Domain) => void;
|
||||
onRemove: () => void;
|
||||
onError?: (error: boolean) => void;
|
||||
error?: string;
|
||||
};
|
||||
enum ActionType {
|
||||
ADD = "ADD",
|
||||
REMOVE = "REMOVE",
|
||||
UPDATE = "UPDATE",
|
||||
}
|
||||
|
||||
export const domainReducer = (state: Domain[], action: any): Domain[] => {
|
||||
switch (action.type) {
|
||||
case ActionType.ADD:
|
||||
return [...state, { name: "", id: uniqueId("domain") }];
|
||||
case ActionType.REMOVE:
|
||||
return state.filter((_, i) => i !== action.index);
|
||||
case ActionType.UPDATE:
|
||||
return state.map((n, i) => (i === action.index ? action.d : n));
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default function InputDomain({
|
||||
value,
|
||||
onChange,
|
||||
onRemove,
|
||||
onError,
|
||||
}: Readonly<Props>) {
|
||||
const [name, setName] = useState(value?.name || "");
|
||||
|
||||
const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setName(e.target.value);
|
||||
onChange({ ...value, name: e.target.value });
|
||||
};
|
||||
|
||||
const domainError = useMemo(() => {
|
||||
if (name == "") {
|
||||
return "";
|
||||
}
|
||||
const valid = validator.isValidDomain(name);
|
||||
if (!valid) {
|
||||
return "Please enter a valid domain, e.g. example.com or intra.example.com";
|
||||
}
|
||||
}, [name]);
|
||||
|
||||
useEffect(() => {
|
||||
const hasError = domainError !== "" && domainError !== undefined;
|
||||
onError?.(hasError);
|
||||
return () => onError?.(false);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [domainError]);
|
||||
|
||||
return (
|
||||
<div className={"flex gap-2 w-full"}>
|
||||
<div className={"w-full"}>
|
||||
<Input
|
||||
customPrefix={<GlobeIcon size={15} />}
|
||||
placeholder={"e.g., example.com"}
|
||||
maxWidthClass={"w-full"}
|
||||
value={name}
|
||||
error={domainError}
|
||||
onChange={handleNameChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
className={"h-[42px]"}
|
||||
variant={"default-outline"}
|
||||
onClick={onRemove}
|
||||
>
|
||||
<MinusCircleIcon size={15} />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -34,8 +34,6 @@ export default function RoutesProvider({ children }: Props) {
|
||||
onSuccess?: (route: Route) => void,
|
||||
message?: string,
|
||||
) => {
|
||||
const hasDomains = route.domains ? route.domains.length > 0 : false;
|
||||
|
||||
notify({
|
||||
title: "Network " + route.network_id + "-" + route.network,
|
||||
description: message
|
||||
@@ -50,9 +48,7 @@ export default function RoutesProvider({ children }: Props) {
|
||||
peer: toUpdate.peer ?? (route.peer || undefined),
|
||||
peer_groups:
|
||||
toUpdate.peer_groups ?? (route.peer_groups || undefined),
|
||||
network: !hasDomains ? route.network : undefined,
|
||||
domains: hasDomains ? route.domains : undefined,
|
||||
keep_route: route.keep_route,
|
||||
network: route.network,
|
||||
metric: toUpdate.metric ?? route.metric ?? 9999,
|
||||
masquerade: toUpdate.masquerade ?? route.masquerade ?? true,
|
||||
groups: toUpdate.groups ?? route.groups ?? [],
|
||||
@@ -84,9 +80,7 @@ export default function RoutesProvider({ children }: Props) {
|
||||
enabled: route.enabled,
|
||||
peer: route.peer || undefined,
|
||||
peer_groups: route.peer_groups || undefined,
|
||||
network: route?.network || undefined,
|
||||
domains: route?.domains || undefined,
|
||||
keep_route: route?.keep_route || false,
|
||||
network: route.network,
|
||||
metric: route.metric || 9999,
|
||||
masquerade: route.masquerade,
|
||||
groups: route.groups || [],
|
||||
|
||||
@@ -19,8 +19,6 @@ export const getOperatingSystem = (os: string) => {
|
||||
if (os.toLowerCase().includes("android"))
|
||||
return OperatingSystem.ANDROID as const;
|
||||
if (os.toLowerCase().includes("ios")) return OperatingSystem.IOS as const;
|
||||
if (os.toLowerCase().includes("ipad")) return OperatingSystem.IOS as const;
|
||||
if (os.toLowerCase().includes("iphone")) return OperatingSystem.IOS as const;
|
||||
if (os.toLowerCase().includes("windows"))
|
||||
return OperatingSystem.WINDOWS as const;
|
||||
return OperatingSystem.LINUX as const;
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
export interface Domain {
|
||||
id?: string;
|
||||
name: string;
|
||||
}
|
||||
@@ -17,6 +17,11 @@ export interface Nameserver {
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export interface Domain {
|
||||
id?: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export const NameserverPresets: Record<string, NameserverGroup> = {
|
||||
Default: {
|
||||
name: "",
|
||||
|
||||
+2
-10
@@ -3,34 +3,26 @@ export interface Route {
|
||||
description: string;
|
||||
enabled: boolean;
|
||||
peer?: string;
|
||||
network?: string;
|
||||
domains?: string[];
|
||||
network: string;
|
||||
network_id: string;
|
||||
network_type?: string;
|
||||
metric?: number;
|
||||
masquerade: boolean;
|
||||
groups: string[];
|
||||
keep_route?: boolean;
|
||||
// Frontend only
|
||||
peer_groups?: string[];
|
||||
routesGroups?: string[];
|
||||
groupedRoutes?: GroupedRoute[];
|
||||
group_names?: string[];
|
||||
domain_search?: string;
|
||||
}
|
||||
|
||||
export interface GroupedRoute {
|
||||
id: string;
|
||||
enabled: boolean;
|
||||
network?: string;
|
||||
domains?: string[];
|
||||
keep_route?: boolean;
|
||||
network: string;
|
||||
network_id: string;
|
||||
high_availability_count: number;
|
||||
is_using_route_groups: boolean;
|
||||
routes?: Route[];
|
||||
group_names?: string[];
|
||||
description?: string;
|
||||
description_search?: string;
|
||||
domain_search?: string;
|
||||
}
|
||||
@@ -118,38 +118,29 @@ export default function ActivityDescription({ event }: Props) {
|
||||
* Route
|
||||
*/
|
||||
|
||||
if (event.activity_code == "route.delete") {
|
||||
let hasDomains = m?.domains && m?.domains.length > 0;
|
||||
if (event.activity_code == "route.delete")
|
||||
return (
|
||||
<div className={"inline"}>
|
||||
Route <Value>{m.name}</Value> with the {hasDomains ? "domain(s)" : ""}{" "}
|
||||
<Value>{hasDomains ? m?.domains : m.network_range}</Value>{" "}
|
||||
{hasDomains ? "" : "range"} was deleted
|
||||
Route <Value>{m.name}</Value> with the <Value>{m.network_range}</Value>{" "}
|
||||
range was deleted
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (event.activity_code == "route.update") {
|
||||
let hasDomains = m?.domains && m?.domains.length > 0;
|
||||
if (event.activity_code == "route.update")
|
||||
return (
|
||||
<div className={"inline"}>
|
||||
Route <Value>{m.name}</Value> with the {hasDomains ? "domain(s)" : ""}{" "}
|
||||
<Value>{hasDomains ? m?.domains : m.network_range}</Value>{" "}
|
||||
{hasDomains ? "" : "range"} was updated
|
||||
Route <Value>{m.name}</Value> with the <Value>{m.network_range}</Value>{" "}
|
||||
range was updated
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (event.activity_code == "route.add") {
|
||||
let hasDomains = m?.domains && m?.domains.length > 0;
|
||||
if (event.activity_code == "route.add")
|
||||
return (
|
||||
<div className={"inline"}>
|
||||
Route <Value>{m.name}</Value> with the {hasDomains ? "domain(s)" : ""}{" "}
|
||||
<Value>{hasDomains ? m?.domains : m.network_range}</Value>{" "}
|
||||
{hasDomains ? "" : "range"} was created
|
||||
Route <Value>{m.name}</Value> with the <Value>{m.network_range}</Value>{" "}
|
||||
range was created
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* User
|
||||
|
||||
@@ -17,9 +17,8 @@ import Paragraph from "@components/Paragraph";
|
||||
import { PeerGroupSelector } from "@components/PeerGroupSelector";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@components/Tabs";
|
||||
import { Textarea } from "@components/Textarea";
|
||||
import InputDomain, { domainReducer } from "@components/ui/InputDomain";
|
||||
import { useApiCall } from "@utils/api";
|
||||
import { cn } from "@utils/helpers";
|
||||
import { cn, validator } from "@utils/helpers";
|
||||
import cidr from "ip-cidr";
|
||||
import { uniqueId } from "lodash";
|
||||
import {
|
||||
@@ -36,7 +35,7 @@ import {
|
||||
import React, { useEffect, useMemo, useReducer, useState } from "react";
|
||||
import { useSWRConfig } from "swr";
|
||||
import DNSIcon from "@/assets/icons/DNSIcon";
|
||||
import { Nameserver, NameserverGroup } from "@/interfaces/Nameserver";
|
||||
import { Domain, Nameserver, NameserverGroup } from "@/interfaces/Nameserver";
|
||||
import useGroupHelper from "@/modules/groups/useGroupHelper";
|
||||
|
||||
type Props = {
|
||||
@@ -98,6 +97,19 @@ enum ActionType {
|
||||
UPDATE = "UPDATE",
|
||||
}
|
||||
|
||||
export const domainReducer = (state: Domain[], action: any) => {
|
||||
switch (action.type) {
|
||||
case ActionType.ADD:
|
||||
return [...state, { name: "", id: uniqueId("ns") }];
|
||||
case ActionType.REMOVE:
|
||||
return state.filter((_, i) => i !== action.index);
|
||||
case ActionType.UPDATE:
|
||||
return state.map((n, i) => (i === action.index ? action.d : n));
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export function NameserverModalContent({
|
||||
onSuccess,
|
||||
preset,
|
||||
@@ -187,7 +199,7 @@ export function NameserverModalContent({
|
||||
// Domains
|
||||
const [domains, setDomains] = useReducer(domainReducer, [], () => {
|
||||
if (preset?.domains?.length) {
|
||||
return preset.domains.map((d) => ({ name: d, id: uniqueId("domain") }));
|
||||
return preset.domains.map((d) => ({ name: d, id: uniqueId("ns") }));
|
||||
}
|
||||
return [];
|
||||
});
|
||||
@@ -358,7 +370,7 @@ export function NameserverModalContent({
|
||||
<div className={"flex flex-col gap-2 w-full"}>
|
||||
{domains.map((domain, i) => {
|
||||
return (
|
||||
<InputDomain
|
||||
<DomainInput
|
||||
key={domain.id}
|
||||
value={domain}
|
||||
onChange={(d) =>
|
||||
@@ -607,3 +619,63 @@ function NameserverInput({
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function DomainInput({
|
||||
value,
|
||||
onChange,
|
||||
onRemove,
|
||||
onError,
|
||||
}: {
|
||||
value: Domain;
|
||||
onChange: (d: Domain) => void;
|
||||
onRemove: () => void;
|
||||
onError?: (error: boolean) => void;
|
||||
error?: string;
|
||||
}) {
|
||||
const [name, setName] = useState(value.name);
|
||||
|
||||
const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setName(e.target.value);
|
||||
onChange({ ...value, name: e.target.value });
|
||||
};
|
||||
|
||||
const domainError = useMemo(() => {
|
||||
if (name == "") {
|
||||
return "";
|
||||
}
|
||||
const valid = validator.isValidDomain(name);
|
||||
if (!valid) {
|
||||
onError && onError(true);
|
||||
return "Please enter a valid domain, e.g. example.com or intra.example.com";
|
||||
}
|
||||
onError && onError(false);
|
||||
}, [name, onError]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => onError && onError(false);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={"flex gap-2 w-full"}>
|
||||
<div className={"w-full"}>
|
||||
<Input
|
||||
customPrefix={<GlobeIcon size={15} />}
|
||||
placeholder={"e.g., example.com"}
|
||||
maxWidthClass={"w-full"}
|
||||
value={name}
|
||||
error={domainError}
|
||||
onChange={handleNameChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
className={"h-[42px]"}
|
||||
variant={"default-outline"}
|
||||
onClick={onRemove}
|
||||
>
|
||||
<MinusCircleIcon size={15} />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -26,7 +26,7 @@ export const AddExitNodeButton = ({ peer, firstTime = false }: Props) => {
|
||||
) : (
|
||||
<>
|
||||
<IconDirectionSign size={16} className={"text-yellow-400"} />
|
||||
Set Up Exit Node
|
||||
Setup Exit Node
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
|
||||
@@ -37,7 +37,7 @@ export const ExitNodeDropdownButton = ({ peer }: Props) => {
|
||||
className={"shrink-0 text-yellow-400"}
|
||||
/>
|
||||
<div className={"flex justify-between items-center w-full"}>
|
||||
Set Up Exit Node
|
||||
Setup Exit Node
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -12,8 +12,8 @@ import { Route } from "@/interfaces/Route";
|
||||
import PeerRouteActionCell from "@/modules/peer/PeerRouteActionCell";
|
||||
import PeerRouteActiveCell from "@/modules/peer/PeerRouteActiveCell";
|
||||
import PeerRouteNameCell from "@/modules/peer/PeerRouteNameCell";
|
||||
import PeerRouteNetworkCell from "@/modules/peer/PeerRouteNetworkCell";
|
||||
import usePeerRoutes from "@/modules/peer/usePeerRoutes";
|
||||
import GroupedRouteNetworkRangeCell from "@/modules/route-group/GroupedRouteNetworkRangeCell";
|
||||
import RouteDistributionGroupsCell from "@/modules/routes/RouteDistributionGroupsCell";
|
||||
|
||||
type Props = {
|
||||
@@ -32,14 +32,9 @@ export const RouteTableColumns: ColumnDef<Route>[] = [
|
||||
{
|
||||
accessorKey: "network",
|
||||
header: ({ column }) => {
|
||||
return <DataTableHeader column={column}>Network</DataTableHeader>;
|
||||
return <DataTableHeader column={column}>Network Range</DataTableHeader>;
|
||||
},
|
||||
cell: ({ row }) => (
|
||||
<GroupedRouteNetworkRangeCell
|
||||
domains={row.original?.domains}
|
||||
network={row.original?.network}
|
||||
/>
|
||||
),
|
||||
cell: ({ row }) => <PeerRouteNetworkCell route={row.original} />,
|
||||
},
|
||||
{
|
||||
id: "groups",
|
||||
|
||||
@@ -1,23 +1,15 @@
|
||||
import { DomainListBadge } from "@components/ui/DomainListBadge";
|
||||
import { IconDirectionSign } from "@tabler/icons-react";
|
||||
import { InfoIcon } from "lucide-react";
|
||||
import * as React from "react";
|
||||
import { ExitNodeHelpTooltip } from "@/modules/exit-node/ExitNodeHelpTooltip";
|
||||
|
||||
type Props = {
|
||||
network?: string;
|
||||
domains?: string[];
|
||||
network: string;
|
||||
};
|
||||
export default function GroupedRouteNetworkRangeCell({
|
||||
network,
|
||||
domains,
|
||||
}: Props) {
|
||||
export default function GroupedRouteNetworkRangeCell({ network }: Props) {
|
||||
const isExitNode = network === "0.0.0.0/0";
|
||||
const hasDomains = domains ? domains.length > 0 : false;
|
||||
|
||||
return hasDomains && domains ? (
|
||||
<DomainListBadge domains={domains} />
|
||||
) : isExitNode ? (
|
||||
return isExitNode ? (
|
||||
<ExitNodeHelpTooltip>
|
||||
<div className={"flex gap-2 items-center dark:text-nb-gray-300 group"}>
|
||||
<IconDirectionSign size={16} className={"text-yellow-400"} />
|
||||
|
||||
@@ -39,14 +39,6 @@ export const GroupedRouteTableColumns: ColumnDef<GroupedRoute>[] = [
|
||||
accessorKey: "description",
|
||||
sortingFn: "text",
|
||||
},
|
||||
{
|
||||
accessorKey: "description_search",
|
||||
sortingFn: "text",
|
||||
},
|
||||
{
|
||||
accessorKey: "domain_search",
|
||||
sortingFn: "text",
|
||||
},
|
||||
{
|
||||
id: "enabled",
|
||||
accessorKey: "enabled",
|
||||
@@ -58,22 +50,13 @@ export const GroupedRouteTableColumns: ColumnDef<GroupedRoute>[] = [
|
||||
return row.group_names?.map((name) => name).join(", ");
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "domains",
|
||||
accessorFn: (row) => {
|
||||
return row.domains?.map((name) => name).join(", ");
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "network",
|
||||
header: ({ column }) => {
|
||||
return <DataTableHeader column={column}>Network</DataTableHeader>;
|
||||
return <DataTableHeader column={column}>Network Range</DataTableHeader>;
|
||||
},
|
||||
cell: ({ row }) => (
|
||||
<GroupedRouteNetworkRangeCell
|
||||
network={row.original.network}
|
||||
domains={row.original?.domains}
|
||||
/>
|
||||
<GroupedRouteNetworkRangeCell network={row.original.network} />
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -149,10 +132,7 @@ export default function NetworkRoutesTable({
|
||||
columnVisibility={{
|
||||
enabled: false,
|
||||
description: false,
|
||||
description_search: false,
|
||||
group_names: false,
|
||||
domains: false,
|
||||
domain_search: false,
|
||||
}}
|
||||
renderExpandedRow={(row) => {
|
||||
const data = cloneDeep(row);
|
||||
|
||||
@@ -53,26 +53,15 @@ export default function useGroupedRoutes({ routes }: Props) {
|
||||
});
|
||||
|
||||
const allGroupNames = [...peerGroupNames, ...distributionGroupNames];
|
||||
const hasDomains = routes[0].domains
|
||||
? routes[0].domains.length > 0
|
||||
: false;
|
||||
|
||||
const childDescriptions =
|
||||
routes?.map((r) => r?.description).join(", ") || "";
|
||||
const domainString = routes?.map((r) => r.domains?.join(", ")).join(", ");
|
||||
|
||||
results.push({
|
||||
id,
|
||||
enabled: routes.find((r) => r.enabled) != undefined,
|
||||
network: !hasDomains ? routes[0].network : undefined,
|
||||
domains: hasDomains ? routes[0].domains || undefined : undefined,
|
||||
domain_search: domainString,
|
||||
keep_route: routes[0].keep_route || false,
|
||||
network: routes[0].network,
|
||||
network_id: routes[0].network_id,
|
||||
high_availability_count: allPeers,
|
||||
is_using_route_groups: !!groupPeerRoute,
|
||||
description: groupPeerRoute ? groupPeerRoute?.description : undefined,
|
||||
description_search: childDescriptions,
|
||||
routes: routes,
|
||||
group_names: allGroupNames,
|
||||
});
|
||||
|
||||
@@ -89,13 +89,6 @@ function Content({ onSuccess, groupedRoute, peer }: ModalProps) {
|
||||
.map((g) => g.id)
|
||||
.filter((id) => id !== undefined) as string[];
|
||||
|
||||
let useRange = false;
|
||||
if (routeNetwork?.domains) {
|
||||
useRange = routeNetwork.domains.length <= 0;
|
||||
} else {
|
||||
useRange = true;
|
||||
}
|
||||
|
||||
createRoute(
|
||||
{
|
||||
network_id: routeNetwork.network_id,
|
||||
@@ -103,9 +96,7 @@ function Content({ onSuccess, groupedRoute, peer }: ModalProps) {
|
||||
enabled: true,
|
||||
peer: routingPeer?.id || undefined,
|
||||
peer_groups: undefined,
|
||||
network: useRange ? routeNetwork.network : undefined,
|
||||
domains: useRange ? undefined : routeNetwork.domains,
|
||||
keep_route: routeNetwork.keep_route || false,
|
||||
network: routeNetwork.network,
|
||||
metric: 9999,
|
||||
masquerade: true,
|
||||
groups: groupIds,
|
||||
@@ -148,7 +139,7 @@ function Content({ onSuccess, groupedRoute, peer }: ModalProps) {
|
||||
<div>
|
||||
<Label>Routing Peer</Label>
|
||||
<HelpText>
|
||||
Assign a single peer as a routing peer for the network route.
|
||||
Assign a single peer as a routing peer for the Network CIDR.
|
||||
</HelpText>
|
||||
<PeerSelector
|
||||
onChange={setRoutingPeer}
|
||||
|
||||
Loaded 20 of 24 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user