Skip to content

Commit f74f9cf

Browse files
heisbrotmlsmaycon
andauthored
Add region and public ip to peer table and detailed peer view (#340)
* Fix group badge icon size * Fix copy icon size * Add region information to peer table and single peer view * Push to docker * Change login expired icon size * Fix country flag in single peer view * Change country flag size in peer table * Disable revalidation for countries * Fix icon size on peer detail view * Rollback workflow * Revert login expiration --------- Co-authored-by: Maycon Santos <[email protected]>
1 parent 7578595 commit f74f9cf

File tree

14 files changed

+253
-42
lines changed

14 files changed

+253
-42
lines changed

src/app/(dashboard)/peer/page.tsx

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,28 @@ import TextWithTooltip from "@components/ui/TextWithTooltip";
2626
import { IconCloudLock, IconInfoCircle } from "@tabler/icons-react";
2727
import useFetchApi from "@utils/api";
2828
import dayjs from "dayjs";
29-
import { trim } from "lodash";
29+
import { isEmpty, trim } from "lodash";
3030
import {
3131
Cpu,
32+
FlagIcon,
3233
Globe,
3334
History,
3435
MapPin,
3536
MonitorSmartphoneIcon,
37+
NetworkIcon,
3638
PencilIcon,
3739
TerminalSquare,
3840
} from "lucide-react";
3941
import { useRouter, useSearchParams } from "next/navigation";
4042
import { toASCII } from "punycode";
4143
import React, { useMemo, useState } from "react";
44+
import Skeleton from "react-loading-skeleton";
4245
import { useSWRConfig } from "swr";
46+
import RoundedFlag from "@/assets/countries/RoundedFlag";
4347
import CircleIcon from "@/assets/icons/CircleIcon";
4448
import NetBirdIcon from "@/assets/icons/NetBirdIcon";
4549
import PeerIcon from "@/assets/icons/PeerIcon";
50+
import { useCountries } from "@/contexts/CountryProvider";
4651
import PeerProvider, { usePeer } from "@/contexts/PeerProvider";
4752
import RoutesProvider from "@/contexts/RoutesProvider";
4853
import { useHasChanges } from "@/hooks/useHasChanges";
@@ -139,7 +144,7 @@ function PeerOverview() {
139144
<CircleIcon
140145
active={peer.connected}
141146
size={12}
142-
className={"mb-[3px]"}
147+
className={"mb-[3px] shrink-0"}
143148
/>
144149
<TextWithTooltip text={name} maxChars={30} />
145150

@@ -291,6 +296,12 @@ function PeerOverview() {
291296
}
292297

293298
function PeerInformationCard({ peer }: { peer: Peer }) {
299+
const { isLoading, getRegionByPeer } = useCountries();
300+
301+
const countryText = useMemo(() => {
302+
return getRegionByPeer(peer);
303+
}, [getRegionByPeer, peer]);
304+
294305
return (
295306
<Card>
296307
<Card.List>
@@ -304,6 +315,44 @@ function PeerInformationCard({ peer }: { peer: Peer }) {
304315
value={peer.ip}
305316
/>
306317

318+
<Card.ListItem
319+
label={
320+
<>
321+
<NetworkIcon size={16} />
322+
Public IP-Address
323+
</>
324+
}
325+
value={peer.connection_ip}
326+
/>
327+
328+
<Card.ListItem
329+
label={
330+
<>
331+
<FlagIcon size={16} />
332+
Region
333+
</>
334+
}
335+
tooltip={false}
336+
value={
337+
isEmpty(peer.country_code) ? (
338+
"Unknown"
339+
) : (
340+
<>
341+
{isLoading ? (
342+
<Skeleton width={140} />
343+
) : (
344+
<div className={"flex gap-2 items-center"}>
345+
<div className={"border-0 border-nb-gray-800 rounded-full"}>
346+
<RoundedFlag country={peer.country_code} size={12} />
347+
</div>
348+
{countryText}
349+
</div>
350+
)}
351+
</>
352+
)
353+
}
354+
/>
355+
307356
<Card.ListItem
308357
label={
309358
<>
@@ -347,6 +396,7 @@ function PeerInformationCard({ peer }: { peer: Peer }) {
347396
")"
348397
}
349398
/>
399+
350400
<Card.ListItem
351401
label={
352402
<>

src/components/Card.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,15 @@ type CardListItemProps = {
3030
value: React.ReactNode;
3131
className?: string;
3232
copy?: boolean;
33+
tooltip?: boolean;
3334
};
3435

3536
function CardListItem({
3637
label,
3738
value,
3839
className,
3940
copy = false,
41+
tooltip = true,
4042
}: CardListItemProps) {
4143
const [, copyToClipBoard] = useCopyToClipboard(value as string);
4244

@@ -57,7 +59,11 @@ function CardListItem({
5759
copy && copyToClipBoard(`${label} has been copied to clipboard.`)
5860
}
5961
>
60-
<TextWithTooltip text={value as string} maxChars={40} />
62+
{tooltip ? (
63+
<TextWithTooltip text={value as string} maxChars={40} />
64+
) : (
65+
value
66+
)}
6167
{copy && <Copy size={13} />}
6268
</div>
6369
</li>

src/components/CopyToClipboardText.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,16 @@ export default function CopyToClipboardText({ children, message }: Props) {
2828

2929
{copied ? (
3030
<CheckIcon
31-
className={"text-nb-gray-100 opacity-0 group-hover:opacity-100"}
31+
className={
32+
"text-nb-gray-100 opacity-0 group-hover:opacity-100 shrink-0"
33+
}
3234
size={12}
3335
/>
3436
) : (
3537
<CopyIcon
36-
className={"text-nb-gray-100 opacity-0 group-hover:opacity-100"}
38+
className={
39+
"text-nb-gray-100 opacity-0 group-hover:opacity-100 shrink-0"
40+
}
3741
size={12}
3842
/>
3943
)}

src/components/FullTooltip.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export default function FullTooltip({
5050
className={cn(
5151
isAction ? "cursor-pointer" : "cursor-default",
5252
"inline-flex items-center gap-2 dark:text-neutral-300 text-neutral-500 hover:text-neutral-100 transition-all hover:bg-nb-gray-800/60 py-2 px-3 rounded-md",
53+
className,
5354
)}
5455
>
5556
{children}

src/components/ui/CountrySelector.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,16 @@ import {
22
SelectDropdown,
33
SelectOption,
44
} from "@components/select/SelectDropdown";
5-
import useFetchApi from "@utils/api";
65
import { createElement, useMemo } from "react";
76
import RoundedFlag from "@/assets/countries/RoundedFlag";
8-
import { Country } from "@/interfaces/Country";
7+
import { useCountries } from "@/contexts/CountryProvider";
98

109
type Props = {
1110
value: string;
1211
onChange: (value: string) => void;
1312
};
1413
export const CountrySelector = ({ value, onChange }: Props) => {
15-
const { data: countries, isLoading } = useFetchApi<Country[]>(
16-
"/locations/countries",
17-
);
14+
const { countries, isLoading } = useCountries();
1815

1916
const countryList = useMemo(() => {
2017
return countries?.map((country) => {

src/components/ui/GroupBadge.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export default function GroupBadge({
2727
className={cn("transition-all group whitespace-nowrap", className)}
2828
onClick={onClick}
2929
>
30-
<FolderGit2 size={12} />
30+
<FolderGit2 size={12} className={"shrink-0"} />
3131
<TextWithTooltip text={group.name} maxChars={20} />
3232
{children}
3333
{showX && (

src/components/ui/LoginExpiredBadge.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export default function LoginExpiredBadge({ loginExpired }: Props) {
1010
<Tooltip delayDuration={1}>
1111
<TooltipTrigger>
1212
<Badge variant={"red"} className={"px-3"}>
13-
<AlertTriangle size={14} className={"mr-1"} />
13+
<AlertTriangle size={13} className={"mr-1"} />
1414
Login required
1515
</Badge>
1616
</TooltipTrigger>

src/components/ui/TextWithTooltip.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,12 @@ export default function TextWithTooltip({
2424
<FullTooltip
2525
disabled={charCount <= maxChars || hideTooltip}
2626
interactive={false}
27+
className={"truncate w-full"}
2728
content={
2829
<div className={"max-w-xs break-all whitespace-normal"}>{text}</div>
2930
}
3031
>
31-
<span className={cn(className)}>
32+
<span className={cn(className, "truncate")}>
3233
{charCount > maxChars ? text && `${text.slice(0, maxChars)}...` : text}
3334
</span>
3435
</FullTooltip>

src/contexts/CountryProvider.tsx

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import useFetchApi from "@utils/api";
2+
import React, { useCallback } from "react";
3+
import { Country } from "@/interfaces/Country";
4+
import { Peer } from "@/interfaces/Peer";
5+
6+
type Props = {
7+
children: React.ReactNode;
8+
};
9+
10+
const CountryContext = React.createContext(
11+
{} as {
12+
countries: Country[] | undefined;
13+
isLoading: boolean;
14+
getRegionByPeer: (peer: Peer) => string;
15+
},
16+
);
17+
18+
export default function CountryProvider({ children }: Props) {
19+
const { data: countries, isLoading } = useFetchApi<Country[]>(
20+
"/locations/countries",
21+
false,
22+
false,
23+
);
24+
25+
const getRegionByPeer = useCallback(
26+
(peer: Peer) => {
27+
if (!countries) return "Unknown";
28+
const country = countries.find(
29+
(c) => c.country_code === peer.country_code,
30+
);
31+
if (!country) return "Unknown";
32+
if (!peer.city_name) return country.country_name;
33+
return `${country.country_name}, ${peer.city_name}`;
34+
},
35+
[countries],
36+
);
37+
38+
return (
39+
<CountryContext.Provider value={{ countries, isLoading, getRegionByPeer }}>
40+
{children}
41+
</CountryContext.Provider>
42+
);
43+
}
44+
45+
export const useCountries = () => {
46+
return React.useContext(CountryContext);
47+
};

src/interfaces/Peer.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,7 @@ export interface Peer {
2020
login_expired: boolean;
2121
login_expiration_enabled: boolean;
2222
approval_required: boolean;
23+
city_name: string;
24+
country_code: string;
25+
connection_ip: string;
2326
}

0 commit comments

Comments
 (0)